summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-16 12:52:39 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-16 12:52:39 +0000
commit89995165ab9f086b284fb9745ac8934646e30675 (patch)
treee3798483911640c002df5358298c58c4d22ab604
parent89572fe97def48ebb0aa2afc5822a36a55b98735 (diff)
downloadchromium_src-89995165ab9f086b284fb9745ac8934646e30675.zip
chromium_src-89995165ab9f086b284fb9745ac8934646e30675.tar.gz
chromium_src-89995165ab9f086b284fb9745ac8934646e30675.tar.bz2
Land Recent QUIC changes.
Stop versioning non crypto parameters by SCFG. This enables the server to send different values for these parameters for same SCFG. As a consequence the server sends the negotiated (authoritative) values of these parameters in SHLO. Merge internal change: 45655201 QUIC: make several magic values configurable. This is half a change. The other half needs to alter server and so I'll put it in a different CL. This makes four magic values from the server handshake into parameters of the server config. A future CL will be able to have the server set them from it's SSL config protobuf. Merge internal change: 45622443 QUIC: don't request a proof if the client doesn't have a ProofVerifier. In order to support cert-less operation, this change alters the client to not request a proof from the server if it doesn't have a ProofVerifier configured. Without a ProofVerifier, the client will simply do opportunistic encryption. Merge internal change: 45614800 * Stop processing if the current packet closed the connection. * Close the connection if invalid RST packet received (consistent with current behavior) -- UDP provides simple CRC. Merge internal change: 45612040 Don't call ConnectionClose on ConnectionCloseFrame if visitor asked to stop after processing ack frame. Merge internal change: 45606025 Don't further process revived packet if visitor refuses the packet header. Merge internal change: 45530388 Fix coding style nits. Use "*sets" instead of "set" for arguments or variables of the CommonCertSets type. Merge internal change: 45523282 Added enum for write packet error. Handling failed writes due to errors other than EAGAIN/EWOULDBLOCK I don't know if this happens for us but might as well handle it. Merge internal change: 45522400 Tear down the connection when there is a decompression error. Merge internal change: 45521857 Bugfix infinite wait Merge internal change: 45509285 Replaced number 3 with kSpdyVersion3. Will work akalin to define and use SpdyMajorVersion enum and use it everywhere. This is a partial merge of internal change: 45485205 Removing an obselete TODO Merge internal change: 45471987 Move QuicConfig out of QuicCryptoStream. The motivation behind this change is to be able to select different values for QuicConfig depending upon SNI (after we receive CHLO). Merge internal change: 45434264 Limiting the number of FEC groups to 2 Merge internal change: 45425759 Closing connection on out of bounds packet. Merge internal change: 45413532 Miscellaneous cleanup: add 'const', remove unneeded headers, and make random minor fixes. Document the CommonCertSets methods better. Merge internal change: 45380570 Move FindMutualTag from CryptoUtils to QuicUtils. We will also use this in version negotiation Merge internal change: 45337156 Replacing CHECK-fails on address migration with graceful shutdown. Added GetAddressFamily utility method. Added check for IPV4 in WritePacket method QuicSocketUtils to copy the IPV4 self_address. Merge internal change: 45306947 QUIC - Negotiate max open streams. Added QuicClientSessionPeer to access QuicConfig in QuicClientSession. Merge internal change: 45233402 Allow retransmitting packets that are retransmissions when we get trucated acks. Merge internal change: 45233252 Reduce connection timeout till crypto handshake is finished to 1min. Merge internal change: 45232483 R=rch@chromium.org Review URL: https://chromiumcodereview.appspot.com/15074007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@200519 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/base/ip_endpoint.cc10
-rw-r--r--net/base/net_util.cc11
-rw-r--r--net/base/net_util.h5
-rw-r--r--net/base/net_util_unittest.cc8
-rw-r--r--net/net.gyp7
-rw-r--r--net/quic/congestion_control/hybrid_slow_start.cc1
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender.cc1
-rw-r--r--net/quic/crypto/cert_compressor.cc24
-rw-r--r--net/quic/crypto/cert_compressor.h18
-rw-r--r--net/quic/crypto/cert_compressor_test.cc8
-rw-r--r--net/quic/crypto/common_cert_set.cc16
-rw-r--r--net/quic/crypto/common_cert_set.h16
-rw-r--r--net/quic/crypto/common_cert_set_test.cc12
-rw-r--r--net/quic/crypto/crypto_handshake.cc65
-rw-r--r--net/quic/crypto/crypto_handshake.h9
-rw-r--r--net/quic/crypto/crypto_handshake_test.cc3
-rw-r--r--net/quic/crypto/crypto_protocol.h1
-rw-r--r--net/quic/crypto/crypto_server_config.cc64
-rw-r--r--net/quic/crypto/crypto_server_config.h44
-rw-r--r--net/quic/crypto/crypto_utils.cc47
-rw-r--r--net/quic/crypto/crypto_utils.h20
-rw-r--r--net/quic/quic_client_session.cc10
-rw-r--r--net/quic/quic_client_session.h9
-rw-r--r--net/quic/quic_client_session_test.cc5
-rw-r--r--net/quic/quic_config.cc362
-rw-r--r--net/quic/quic_config.h170
-rw-r--r--net/quic/quic_config_test.cc167
-rw-r--r--net/quic/quic_connection.cc144
-rw-r--r--net/quic/quic_connection.h13
-rw-r--r--net/quic/quic_connection_helper_test.cc22
-rw-r--r--net/quic/quic_connection_test.cc109
-rw-r--r--net/quic/quic_crypto_client_stream.cc18
-rw-r--r--net/quic/quic_crypto_client_stream.h3
-rw-r--r--net/quic/quic_crypto_client_stream_factory.h1
-rw-r--r--net/quic/quic_crypto_client_stream_test.cc19
-rw-r--r--net/quic/quic_crypto_server_stream.cc9
-rw-r--r--net/quic/quic_crypto_server_stream.h7
-rw-r--r--net/quic/quic_crypto_server_stream_test.cc57
-rw-r--r--net/quic/quic_crypto_stream.cc5
-rw-r--r--net/quic/quic_crypto_stream.h2
-rw-r--r--net/quic/quic_framer.cc33
-rw-r--r--net/quic/quic_framer.h3
-rw-r--r--net/quic/quic_framer_test.cc79
-rw-r--r--net/quic/quic_http_stream_test.cc5
-rw-r--r--net/quic/quic_protocol.h19
-rw-r--r--net/quic/quic_reliable_client_stream_test.cc1
-rw-r--r--net/quic/quic_session.cc15
-rw-r--r--net/quic/quic_session.h14
-rw-r--r--net/quic/quic_session_test.cc33
-rw-r--r--net/quic/quic_spdy_compressor.cc2
-rw-r--r--net/quic/quic_spdy_decompressor.cc10
-rw-r--r--net/quic/quic_spdy_decompressor.h1
-rw-r--r--net/quic/quic_spdy_decompressor_test.cc16
-rw-r--r--net/quic/quic_stream_factory.cc3
-rw-r--r--net/quic/quic_utils.cc82
-rw-r--r--net/quic/quic_utils.h26
-rw-r--r--net/quic/reliable_quic_stream.cc5
-rw-r--r--net/quic/reliable_quic_stream.h1
-rw-r--r--net/quic/test_tools/crypto_test_utils.cc37
-rw-r--r--net/quic/test_tools/crypto_test_utils.h13
-rw-r--r--net/quic/test_tools/mock_crypto_client_stream.cc3
-rw-r--r--net/quic/test_tools/mock_crypto_client_stream.h1
-rw-r--r--net/quic/test_tools/mock_crypto_client_stream_factory.cc5
-rw-r--r--net/quic/test_tools/mock_crypto_client_stream_factory.h1
-rw-r--r--net/quic/test_tools/quic_client_session_peer.cc21
-rw-r--r--net/quic/test_tools/quic_client_session_peer.h29
-rw-r--r--net/quic/test_tools/quic_connection_peer.cc24
-rw-r--r--net/quic/test_tools/quic_connection_peer.h13
-rw-r--r--net/quic/test_tools/quic_session_peer.cc8
-rw-r--r--net/quic/test_tools/quic_session_peer.h3
-rw-r--r--net/quic/test_tools/quic_test_utils.cc12
-rw-r--r--net/quic/test_tools/quic_test_utils.h7
-rw-r--r--net/socket/socket_test_util.cc1
-rw-r--r--net/tools/quic/end_to_end_test.cc85
-rw-r--r--net/tools/quic/quic_client.cc3
-rw-r--r--net/tools/quic/quic_client.h6
-rw-r--r--net/tools/quic/quic_client_session.cc4
-rw-r--r--net/tools/quic/quic_client_session_test.cc31
-rw-r--r--net/tools/quic/quic_epoll_connection_helper.h5
-rw-r--r--net/tools/quic/quic_epoll_connection_helper_test.cc16
-rw-r--r--net/tools/quic/quic_reliable_client_stream.h3
-rw-r--r--net/tools/quic/quic_reliable_client_stream_test.cc5
-rw-r--r--net/tools/quic/quic_server.cc9
-rw-r--r--net/tools/quic/quic_server_session.cc5
-rw-r--r--net/tools/quic/quic_socket_utils.cc2
-rw-r--r--net/tools/quic/test_tools/quic_client_peer.cc27
-rw-r--r--net/tools/quic/test_tools/quic_client_peer.h25
-rw-r--r--net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc21
-rw-r--r--net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h31
-rw-r--r--net/tools/quic/test_tools/quic_test_client.cc3
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.cc10
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.h7
92 files changed, 1761 insertions, 585 deletions
diff --git a/net/base/ip_endpoint.cc b/net/base/ip_endpoint.cc
index 923596e..e86c457 100644
--- a/net/base/ip_endpoint.cc
+++ b/net/base/ip_endpoint.cc
@@ -35,15 +35,7 @@ IPEndPoint::IPEndPoint(const IPEndPoint& endpoint) {
}
AddressFamily IPEndPoint::GetFamily() const {
- switch (address_.size()) {
- case kIPv4AddressSize:
- return ADDRESS_FAMILY_IPV4;
- case kIPv6AddressSize:
- return ADDRESS_FAMILY_IPV6;
- default:
- NOTREACHED() << "Bad IP address";
- return ADDRESS_FAMILY_UNSPECIFIED;
- }
+ return GetAddressFamily(address_);
}
int IPEndPoint::GetSockAddrFamily() const {
diff --git a/net/base/net_util.cc b/net/base/net_util.cc
index a279293..3d4924a 100644
--- a/net/base/net_util.cc
+++ b/net/base/net_util.cc
@@ -2016,6 +2016,17 @@ bool HaveOnlyLoopbackAddresses() {
#endif // defined(various platforms)
}
+AddressFamily GetAddressFamily(const IPAddressNumber& address) {
+ switch (address.size()) {
+ case kIPv4AddressSize:
+ return ADDRESS_FAMILY_IPV4;
+ case kIPv6AddressSize:
+ return ADDRESS_FAMILY_IPV6;
+ default:
+ return ADDRESS_FAMILY_UNSPECIFIED;
+ }
+}
+
bool ParseIPLiteralToNumber(const std::string& ip_literal,
IPAddressNumber* ip_number) {
// |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains
diff --git a/net/base/net_util.h b/net/base/net_util.h
index 5cfd1ea..e2519ba 100644
--- a/net/base/net_util.h
+++ b/net/base/net_util.h
@@ -22,6 +22,7 @@
#include "base/basictypes.h"
#include "base/string16.h"
+#include "net/base/address_family.h"
#include "net/base/escape.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
@@ -458,6 +459,10 @@ NET_EXPORT IPv6SupportResult TestIPv6Support();
// Also returns false if it cannot determine this.
bool HaveOnlyLoopbackAddresses();
+// Returns AddressFamily of the address.
+NET_EXPORT_PRIVATE AddressFamily GetAddressFamily(
+ const IPAddressNumber& address);
+
// Parses an IP address literal (either IPv4 or IPv6) to its numeric value.
// Returns true on success and fills |ip_number| with the numeric value.
NET_EXPORT_PRIVATE bool ParseIPLiteralToNumber(const std::string& ip_literal,
diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc
index 4ddafe6..a40ad79 100644
--- a/net/base/net_util_unittest.cc
+++ b/net/base/net_util_unittest.cc
@@ -3171,6 +3171,14 @@ TEST(NetUtilTest, GetHostOrSpecFromURL) {
GetHostOrSpecFromURL(GURL("file:///tmp/test.html")));
}
+TEST(NetUtilTest, GetAddressFamily) {
+ IPAddressNumber number;
+ EXPECT_TRUE(ParseIPLiteralToNumber("192.168.0.1", &number));
+ EXPECT_EQ(ADDRESS_FAMILY_IPV4, GetAddressFamily(number));
+ EXPECT_TRUE(ParseIPLiteralToNumber("1:abcd::3:4:ff", &number));
+ EXPECT_EQ(ADDRESS_FAMILY_IPV6, GetAddressFamily(number));
+}
+
// Test that invalid IP literals fail to parse.
TEST(NetUtilTest, ParseIPLiteralToNumber_FailParse) {
IPAddressNumber number;
diff --git a/net/net.gyp b/net/net.gyp
index 41775bd..6bdb387 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1659,6 +1659,8 @@
'quic/test_tools/mock_crypto_client_stream_factory.h',
'quic/test_tools/mock_random.cc',
'quic/test_tools/mock_random.h',
+ 'quic/test_tools/quic_client_session_peer.cc',
+ 'quic/test_tools/quic_client_session_peer.h',
'quic/test_tools/quic_connection_peer.cc',
'quic/test_tools/quic_connection_peer.h',
'quic/test_tools/quic_framer_peer.cc',
@@ -1678,6 +1680,7 @@
'quic/quic_bandwidth_test.cc',
'quic/quic_client_session_test.cc',
'quic/quic_clock_test.cc',
+ 'quic/quic_config_test.cc',
'quic/quic_connection_helper_test.cc',
'quic/quic_connection_test.cc',
'quic/quic_crypto_client_stream_test.cc',
@@ -2685,6 +2688,10 @@
'tools/quic/test_tools/http_message_test_utils.h',
'tools/quic/test_tools/mock_epoll_server.cc',
'tools/quic/test_tools/mock_epoll_server.h',
+ 'tools/quic/test_tools/quic_client_peer.cc',
+ 'tools/quic/test_tools/quic_client_peer.h',
+ 'tools/quic/test_tools/quic_epoll_connection_helper_peer.cc',
+ 'tools/quic/test_tools/quic_epoll_connection_helper_peer.h',
'tools/quic/test_tools/quic_test_client.cc',
'tools/quic/test_tools/quic_test_client.h',
'tools/quic/test_tools/quic_test_utils.cc',
diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc
index 62b28f8..8968dc9 100644
--- a/net/quic/congestion_control/hybrid_slow_start.cc
+++ b/net/quic/congestion_control/hybrid_slow_start.cc
@@ -41,7 +41,6 @@ void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) {
}
bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) {
- // TODO(pwestin): do we need to handle wraparound?
return end_sequence_number_ <= ack;
}
diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc
index 78bd7d9..e6ed9d3 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -228,6 +228,7 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) {
}
hybrid_slow_start_.Update(rtt, delay_min_);
if (hybrid_slow_start_.Exit()) {
+ DLOG(INFO) << "Set slowstart threshold:" << congestion_window_;
slowstart_threshold_ = congestion_window_;
}
}
diff --git a/net/quic/crypto/cert_compressor.cc b/net/quic/crypto/cert_compressor.cc
index 2f36c19..58d259a 100644
--- a/net/quic/crypto/cert_compressor.cc
+++ b/net/quic/crypto/cert_compressor.cc
@@ -20,7 +20,7 @@ namespace {
// kCommonCertSubstrings contains ~1500 bytes of common certificate substrings
// in order to help zlib. This was generated via a fairly dumb algorithm from
// the Alexa Top 5000 set - we could probably do better.
-static unsigned char kCommonCertSubstrings[] = {
+static const unsigned char kCommonCertSubstrings[] = {
0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
@@ -176,7 +176,7 @@ struct CertEntry {
vector<CertEntry> MatchCerts(const vector<string>& certs,
StringPiece client_common_set_hashes,
StringPiece client_cached_cert_hashes,
- const CommonCertSets* common_set) {
+ const CommonCertSets* common_sets) {
vector<CertEntry> entries;
entries.reserve(certs.size());
@@ -214,8 +214,8 @@ vector<CertEntry> MatchCerts(const vector<string>& certs,
}
}
- if (common_set && common_set->MatchCert(*i, client_common_set_hashes,
- &entry.set_hash, &entry.index)) {
+ if (common_sets && common_sets->MatchCert(*i, client_common_set_hashes,
+ &entry.set_hash, &entry.index)) {
entry.type = CertEntry::COMMON;
entries.push_back(entry);
continue;
@@ -328,11 +328,11 @@ vector<uint64> HashCerts(const vector<string>& certs) {
// ParseEntries parses the serialised form of a vector of CertEntries from
// |in_out| and writes them to |out_entries|. CACHED and COMMON entries are
-// resolved using |cached_certs| and |common_set| and written to |out_certs|.
+// resolved using |cached_certs| and |common_sets| and written to |out_certs|.
// |in_out| is updated to contain the trailing data.
bool ParseEntries(StringPiece* in_out,
const vector<string>& cached_certs,
- const CommonCertSets* common_set,
+ const CommonCertSets* common_sets,
vector<CertEntry>* out_entries,
vector<string>* out_certs) {
StringPiece in = *in_out;
@@ -383,7 +383,7 @@ bool ParseEntries(StringPiece* in_out,
break;
}
case CertEntry::COMMON: {
- if (!common_set) {
+ if (!common_sets) {
return false;
}
if (in.size() < sizeof(uint64)) {
@@ -398,7 +398,7 @@ bool ParseEntries(StringPiece* in_out,
memcpy(&entry.index, in.data(), sizeof(uint32));
in.remove_prefix(sizeof(uint32));
- StringPiece cert = common_set->GetCert(entry.set_hash, entry.index);
+ StringPiece cert = common_sets->GetCert(entry.set_hash, entry.index);
if (cert.empty()) {
return false;
}
@@ -449,9 +449,9 @@ class ScopedZLib {
string CertCompressor::CompressChain(const vector<string>& certs,
StringPiece client_common_set_hashes,
StringPiece client_cached_cert_hashes,
- const CommonCertSets* common_set) {
+ const CommonCertSets* common_sets) {
const vector<CertEntry> entries = MatchCerts(
- certs, client_common_set_hashes, client_cached_cert_hashes, common_set);
+ certs, client_common_set_hashes, client_cached_cert_hashes, common_sets);
DCHECK_EQ(entries.size(), certs.size());
size_t uncompressed_size = 0;
@@ -549,10 +549,10 @@ string CertCompressor::CompressChain(const vector<string>& certs,
// static
bool CertCompressor::DecompressChain(StringPiece in,
const vector<string>& cached_certs,
- const CommonCertSets* common_set,
+ const CommonCertSets* common_sets,
vector<string>* out_certs) {
vector<CertEntry> entries;
- if (!ParseEntries(&in, cached_certs, common_set, &entries, out_certs)) {
+ if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) {
return false;
}
DCHECK_EQ(entries.size(), out_certs->size());
diff --git a/net/quic/crypto/cert_compressor.h b/net/quic/crypto/cert_compressor.h
index 587684d..612bc529 100644
--- a/net/quic/crypto/cert_compressor.h
+++ b/net/quic/crypto/cert_compressor.h
@@ -22,31 +22,31 @@ namespace net {
// that they already have. In the event that one of them is to be
// compressed, it can be replaced with just the hash.
// 2) The peer may provide a number of hashes that represent sets of
-// pre-shared certificates (CommonCertSets). If one of those certificates
-// is to be compressed, and it's known to the given CommonCertSets, then it
-// can be replaced with a set hash and certificate index.
+// pre-shared certificates. If one of those certificates is to be
+// compressed, and it's known to the given CommonCertSets, then it can be
+// replaced with a set hash and certificate index.
// 3) Otherwise the certificates are compressed with zlib using a pre-shared
// dictionary that consists of the certificates handled with the above
// methods and a small chunk of common substrings.
class NET_EXPORT_PRIVATE CertCompressor {
public:
// CompressChain compresses the certificates in |certs| and returns a
- // compressed representation. |common_set| contains the common certificate
+ // compressed representation. |common_sets| contains the common certificate
// sets known locally and |client_common_set_hashes| contains the hashes of
- // the common sets known to the peer. |client_cached| contains 64-bit, FNV-1a
- // hashes of certificates that the peer already possesses.
+ // the common sets known to the peer. |client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
static std::string CompressChain(const std::vector<std::string>& certs,
base::StringPiece client_common_set_hashes,
base::StringPiece client_cached_cert_hashes,
- const CommonCertSets* common_set);
+ const CommonCertSets* common_sets);
// DecompressChain decompresses the result of |CompressChain|, given in |in|,
// into a series of certificates that are written to |out_certs|.
// |cached_certs| contains certificates that the peer may have omitted and
- // |common_set| contains the common certificate sets known locally.
+ // |common_sets| contains the common certificate sets known locally.
static bool DecompressChain(base::StringPiece in,
const std::vector<std::string>& cached_certs,
- const CommonCertSets* common_set,
+ const CommonCertSets* common_sets,
std::vector<std::string>* out_certs);
};
diff --git a/net/quic/crypto/cert_compressor_test.cc b/net/quic/crypto/cert_compressor_test.cc
index ef12ccc..5bfef8f 100644
--- a/net/quic/crypto/cert_compressor_test.cc
+++ b/net/quic/crypto/cert_compressor_test.cc
@@ -47,12 +47,12 @@ TEST(CertCompressor, Common) {
vector<string> chain;
chain.push_back("testcert");
static const uint64 set_hash = 42;
- scoped_ptr<CommonCertSets> common_set(
+ scoped_ptr<CommonCertSets> common_sets(
CryptoTestUtils::MockCommonCertSets(chain[0], set_hash, 1));
const string compressed = CertCompressor::CompressChain(
chain,
StringPiece(reinterpret_cast<const char*>(&set_hash), sizeof(set_hash)),
- StringPiece(), common_set.get());
+ StringPiece(), common_sets.get());
const string common("03" /* common */
"2A00000000000000" /* set hash 42 */
"01000000" /* index 1 */
@@ -62,7 +62,7 @@ TEST(CertCompressor, Common) {
vector<string> chain2, cached_certs;
ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs,
- common_set.get(), &chain2));
+ common_sets.get(), &chain2));
EXPECT_EQ(chain.size(), chain2.size());
EXPECT_EQ(chain[0], chain2[0]);
}
@@ -124,7 +124,7 @@ TEST(CertCompressor, BadInputs) {
without_a_common_cert_set.size()),
cached_certs, NULL, &chain));
- scoped_ptr<CommonCertSets> common_set(
+ scoped_ptr<CommonCertSets> common_sets(
CryptoTestUtils::MockCommonCertSets("foo", 42, 1));
/* incorrect hash and index */
diff --git a/net/quic/crypto/common_cert_set.cc b/net/quic/crypto/common_cert_set.cc
index 78aa927..ee1ac67 100644
--- a/net/quic/crypto/common_cert_set.cc
+++ b/net/quic/crypto/common_cert_set.cc
@@ -53,17 +53,17 @@ StringPiece CommonCertSetsQUIC::GetCommonHashes() const {
}
StringPiece CommonCertSetsQUIC::GetCert(uint64 hash, uint32 index) const {
+ StringPiece cert;
for (size_t i = 0; i < arraysize(kSets); i++) {
if (kSets[i].hash == hash) {
- if (index >= kSets[i].num_certs) {
- return StringPiece();
+ if (index < kSets[i].num_certs) {
+ cert.set(kSets[i].certs[index], kSets[i].lens[index]);
}
- return StringPiece(reinterpret_cast<const char*>(kSets[i].certs[index]),
- kSets[i].lens[index]);
+ break;
}
}
- return StringPiece();
+ return cert;
}
// Compare returns a value less than, equal to or greater than zero if |a| is
@@ -110,11 +110,7 @@ bool CommonCertSetsQUIC::MatchCert(StringPiece cert,
// Binary search for a matching certificate.
size_t min = 0;
size_t max = kSets[j].num_certs - 1;
- for (;;) {
- if (max < min) {
- break;
- }
-
+ while (max >= min) {
size_t mid = min + ((max - min) / 2);
int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]);
if (n < 0) {
diff --git a/net/quic/crypto/common_cert_set.h b/net/quic/crypto/common_cert_set.h
index 6972926..4f5d330 100644
--- a/net/quic/crypto/common_cert_set.h
+++ b/net/quic/crypto/common_cert_set.h
@@ -20,24 +20,26 @@ class NET_EXPORT_PRIVATE CommonCertSets {
virtual ~CommonCertSets();
// GetCommonHashes returns a StringPiece containing the hashes of common sets
- // supported by this object.
+ // supported by this object. The 64-bit hashes are concatenated in the
+ // StringPiece.
virtual base::StringPiece GetCommonHashes() const = 0;
- // GetCert returns a specific certificate in the common set identified by
- // |hash|. If no such certificate is known, an empty StringPiece is returned.
+ // GetCert returns a specific certificate (at index |index|) in the common
+ // set identified by |hash|. If no such certificate is known, an empty
+ // StringPiece is returned.
virtual base::StringPiece GetCert(uint64 hash, uint32 index) const = 0;
// MatchCert tries to find |cert| in one of the common certificate sets
- // identified by |common_set_hashes|. On success it puts the hash in
- // |out_hash|, the index in the set in |out_index| and returns true. Otherwise
- // it returns false.
+ // identified by |common_set_hashes|. On success it puts the hash of the
+ // set in |out_hash|, the index of |cert| in the set in |out_index| and
+ // returns true. Otherwise it returns false.
virtual bool MatchCert(base::StringPiece cert,
base::StringPiece common_set_hashes,
uint64* out_hash,
uint32* out_index) const = 0;
};
-// CommonCertSetsQUIC implements the CommonCertSet interface using the default
+// CommonCertSetsQUIC implements the CommonCertSets interface using the default
// certificate sets.
class NET_EXPORT_PRIVATE CommonCertSetsQUIC : public CommonCertSets {
public:
diff --git a/net/quic/crypto/common_cert_set_test.cc b/net/quic/crypto/common_cert_set_test.cc
index 51b41d8..57615c9 100644
--- a/net/quic/crypto/common_cert_set_test.cc
+++ b/net/quic/crypto/common_cert_set_test.cc
@@ -11,7 +11,7 @@ using base::StringPiece;
namespace net {
namespace test {
-static unsigned char kGIACertificate[] = {
+static const unsigned char kGIACertificate[] = {
0x30, 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x03, 0x0b, 0x67, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
@@ -76,30 +76,30 @@ TEST(CommonCertSets, FindGIA) {
StringPiece gia(reinterpret_cast<const char*>(kGIACertificate),
sizeof(kGIACertificate));
- CommonCertSetsQUIC set;
+ CommonCertSetsQUIC sets;
const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54);
uint64 hash;
uint32 index;
- ASSERT_TRUE(set.MatchCert(
+ ASSERT_TRUE(sets.MatchCert(
gia,
StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
&hash, &index));
EXPECT_EQ(in_hash, hash);
- StringPiece gia_copy = set.GetCert(hash, index);
+ StringPiece gia_copy = sets.GetCert(hash, index);
EXPECT_FALSE(gia_copy.empty());
ASSERT_EQ(gia.size(), gia_copy.size());
EXPECT_TRUE(0 == memcmp(gia.data(), gia_copy.data(), gia.size()));
}
TEST(CommonCertSets, NonMatch) {
- CommonCertSetsQUIC set;
+ CommonCertSetsQUIC sets;
StringPiece not_a_cert("hello");
const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54);
uint64 hash;
uint32 index;
- EXPECT_FALSE(set.MatchCert(
+ EXPECT_FALSE(sets.MatchCert(
not_a_cert,
StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
&hash, &index));
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
index 16d32d6..3585eb5 100644
--- a/net/quic/crypto/crypto_handshake.cc
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -107,7 +107,6 @@ QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
const QuicTag** out_tags,
size_t* out_len) const {
QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
- *out_len = 0;
QuicErrorCode ret = QUIC_NO_ERROR;
if (it == tag_value_map_.end()) {
@@ -221,40 +220,12 @@ QuicErrorCode CryptoHandshakeMessage::GetPOD(
return ret;
}
-// TagToString is a utility function for pretty-printing handshake messages
-// that converts a tag to a string. It will try to maintain the human friendly
-// name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number
-// if not.
-static string TagToString(QuicTag tag) {
- char chars[4];
- bool ascii = true;
- const QuicTag orig_tag = tag;
-
- for (size_t i = 0; i < sizeof(chars); i++) {
- chars[i] = tag;
- if (chars[i] == 0 && i == 3) {
- chars[i] = ' ';
- }
- if (!isprint(static_cast<unsigned char>(chars[i]))) {
- ascii = false;
- break;
- }
- tag >>= 8;
- }
-
- if (ascii) {
- return string(chars, sizeof(chars));
- }
-
- return base::UintToString(orig_tag);
-}
-
string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
- string ret = string(2 * indent, ' ') + TagToString(tag_) + "<\n";
+ string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
++indent;
for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
it != tag_value_map_.end(); ++it) {
- ret += string(2 * indent, ' ') + TagToString(it->first) + ": ";
+ ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
bool done = false;
switch (it->first) {
@@ -280,7 +251,7 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
if (j > 0) {
ret += ",";
}
- ret += TagToString(tag);
+ ret += QuicUtils::TagToString(tag);
}
done = true;
}
@@ -331,7 +302,7 @@ const char QuicCryptoConfig::kForwardSecureLabel[] =
QuicCryptoConfig::QuicCryptoConfig()
: version(0),
- common_cert_set_(new CommonCertSetsQUIC) {
+ common_cert_sets(new CommonCertSetsQUIC) {
}
QuicCryptoConfig::~QuicCryptoConfig() {}
@@ -476,13 +447,19 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
}
- out->SetTaglist(kPDMD, kX509, 0);
+ if (proof_verifier_.get()) {
+ out->SetTaglist(kPDMD, kX509, 0);
+ }
- if (common_cert_set_.get()) {
- out->SetStringPiece(kCCS, common_cert_set_->GetCommonHashes());
+ if (common_cert_sets.get()) {
+ out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes());
}
const vector<string>& certs = cached->certs();
+ // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the
+ // client config is being used for multiple connections, another connection
+ // doesn't update the cached certificates and cause us to be unable to
+ // process the server's compressed certificate chain.
out_params->cached_certs = certs;
if (!certs.empty()) {
vector<uint64> hashes;
@@ -492,10 +469,6 @@ void QuicCryptoClientConfig::FillInchoateClientHello(
hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size()));
}
out->SetVector(kCCRT, hashes);
- // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the
- // client config is being used for multiple connections, another connection
- // doesn't update the cached certificates and cause us to be unable to
- // process the server's compressed certificate chain.
}
}
@@ -557,12 +530,12 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
}
size_t key_exchange_index;
- if (!CryptoUtils::FindMutualTag(aead, their_aeads, num_their_aeads,
- CryptoUtils::PEER_PRIORITY, &out_params->aead,
- NULL) ||
- !CryptoUtils::FindMutualTag(
+ if (!QuicUtils::FindMutualTag(aead, their_aeads, num_their_aeads,
+ QuicUtils::PEER_PRIORITY, &out_params->aead,
+ NULL) ||
+ !QuicUtils::FindMutualTag(
kexs, their_key_exchanges, num_their_key_exchanges,
- CryptoUtils::PEER_PRIORITY, &out_params->key_exchange,
+ QuicUtils::PEER_PRIORITY, &out_params->key_exchange,
&key_exchange_index)) {
*error_details = "Unsupported AEAD or KEXS";
return QUIC_CRYPTO_NO_SUPPORT;
@@ -667,7 +640,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
rej.GetStringPiece(kCertificateTag, &cert_bytes)) {
vector<string> certs;
if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs,
- common_cert_set_.get(), &certs)) {
+ common_cert_sets.get(), &certs)) {
*error_details = "Certificate data invalid";
return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
}
diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h
index 6b6c320..0774905 100644
--- a/net/quic/crypto/crypto_handshake.h
+++ b/net/quic/crypto/crypto_handshake.h
@@ -14,7 +14,6 @@
#include "net/base/net_export.h"
#include "net/quic/crypto/crypto_protocol.h"
#include "net/quic/quic_protocol.h"
-#include "net/quic/quic_time.h"
namespace net {
@@ -188,7 +187,7 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig {
// Authenticated encryption with associated data (AEAD) algorithms.
QuicTagVector aead;
- scoped_ptr<CommonCertSets> common_cert_set_;
+ scoped_ptr<CommonCertSets> common_cert_sets;
private:
DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig);
@@ -311,9 +310,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
const ProofVerifier* proof_verifier() const;
// SetProofVerifier takes ownership of a |ProofVerifier| that clients are
- // free to use in order to verify certificate chains from servers. Setting a
- // |ProofVerifier| does not alter the behaviour of the
- // QuicCryptoClientConfig, it's just a place to store it.
+ // free to use in order to verify certificate chains from servers. If a
+ // ProofVerifier is set then the client will request a certificate chain from
+ // the server.
void SetProofVerifier(ProofVerifier* verifier);
private:
diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc
index f3d86a5..e2e45f3 100644
--- a/net/quic/crypto/crypto_handshake_test.cc
+++ b/net/quic/crypto/crypto_handshake_test.cc
@@ -42,10 +42,9 @@ class QuicCryptoServerConfigPeer {
TEST(QuicCryptoServerConfigTest, ServerConfig) {
QuicCryptoServerConfig server("source address token secret");
MockClock clock;
- CryptoHandshakeMessage extra_tags;
scoped_ptr<CryptoHandshakeMessage>(
- server.AddDefaultConfig(QuicRandom::GetInstance(), &clock, extra_tags,
+ server.AddDefaultConfig(QuicRandom::GetInstance(), &clock,
QuicCryptoServerConfig::kDefaultExpiry));
}
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index 32ec7b1..c32884a 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -60,6 +60,7 @@ const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
// lifetime
const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
+const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
// indication
const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc
index e4ee400..d7eca6a 100644
--- a/net/quic/crypto/crypto_server_config.cc
+++ b/net/quic/crypto/crypto_server_config.cc
@@ -52,7 +52,11 @@ QuicCryptoServerConfig::QuicCryptoServerConfig(
// Salsa20+Poly1305).
: strike_register_lock_(),
source_address_token_encrypter_(new Aes128GcmEncrypter),
- source_address_token_decrypter_(new Aes128GcmDecrypter) {
+ source_address_token_decrypter_(new Aes128GcmDecrypter),
+ strike_register_max_entries_(1 << 10),
+ strike_register_window_secs_(600),
+ source_address_token_future_secs_(3600),
+ source_address_token_lifetime_secs_(86400) {
crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
"QUIC source address token key",
source_address_token_encrypter_->GetKeySize(),
@@ -69,7 +73,6 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() {
QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
QuicRandom* rand,
const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags,
uint64 expiry_time) {
CryptoHandshakeMessage msg;
@@ -101,8 +104,6 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
msg.SetTaglist(kAEAD, kAESG, 0);
msg.SetValue(kVERS, static_cast<uint16>(0));
msg.SetStringPiece(kPUBS, encoded_public_values);
- msg.Insert(extra_tags.tag_value_map().begin(),
- extra_tags.tag_value_map().end());
if (expiry_time == 0) {
const QuicWallTime now = clock->WallNow();
@@ -277,10 +278,9 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
QuicRandom* rand,
const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags,
uint64 expiry_time) {
- scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig(
- rand, clock, extra_tags, expiry_time));
+ scoped_ptr<QuicServerConfigProtobuf> config(
+ DefaultConfig(rand, clock, expiry_time));
return AddConfig(config.get());
}
@@ -334,10 +334,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
if (strike_register_.get() == NULL) {
strike_register_.reset(new StrikeRegister(
- // TODO(agl): these magic numbers should come from config.
- 1024 /* max entries */,
+ strike_register_max_entries_,
static_cast<uint32>(now.ToUNIXSeconds()),
- 600 /* window secs */, config->orbit));
+ strike_register_window_secs_,
+ config->orbit));
}
unique_by_strike_register = strike_register_->Insert(
reinterpret_cast<const uint8*>(client_nonce.data()),
@@ -401,7 +401,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
const string compressed = CertCompressor::CompressChain(
*certs, their_common_set_hashes, their_cached_cert_hashes,
- config->common_cert_set_.get());
+ config->common_cert_sets.get());
// kMaxUnverifiedSize is the number of bytes that the certificate chain
// and signature can consume before we will demand a valid
@@ -433,12 +433,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
}
size_t key_exchange_index;
- if (!CryptoUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads,
- CryptoUtils::LOCAL_PRIORITY, &params->aead,
- NULL) ||
- !CryptoUtils::FindMutualTag(
+ if (!QuicUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads,
+ QuicUtils::LOCAL_PRIORITY, &params->aead,
+ NULL) ||
+ !QuicUtils::FindMutualTag(
config->kexs, their_key_exchanges, num_their_key_exchanges,
- CryptoUtils::LOCAL_PRIORITY, &params->key_exchange,
+ QuicUtils::LOCAL_PRIORITY, &params->key_exchange,
&key_exchange_index)) {
*error_details = "Unsupported AEAD or KEXS";
return QUIC_CRYPTO_NO_SUPPORT;
@@ -523,6 +523,28 @@ void QuicCryptoServerConfig::SetEphemeralKeySource(
ephemeral_key_source_.reset(ephemeral_key_source);
}
+void QuicCryptoServerConfig::set_strike_register_max_entries(
+ uint32 max_entries) {
+ DCHECK(!strike_register_.get());
+ strike_register_max_entries_ = max_entries;
+}
+
+void QuicCryptoServerConfig::set_strike_register_window_secs(
+ uint32 window_secs) {
+ DCHECK(!strike_register_.get());
+ strike_register_window_secs_ = window_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_future_secs(
+ uint32 future_secs) {
+ source_address_token_future_secs_ = future_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
+ uint32 lifetime_secs) {
+ source_address_token_lifetime_secs_ = lifetime_secs;
+}
+
string QuicCryptoServerConfig::NewSourceAddressToken(
const IPEndPoint& ip,
QuicRandom* rand,
@@ -600,15 +622,13 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken(
QuicWallTime::FromUNIXSeconds(source_address_token.timestamp()));
const QuicTime::Delta delta(now.AbsoluteDifference(timestamp));
- // TODO(agl): consider whether and how these magic values should be moved to
- // a config.
- if (now.IsBefore(timestamp) && delta.ToSeconds() > 3600) {
- // We only allow timestamps to be from an hour in the future.
+ if (now.IsBefore(timestamp) &&
+ delta.ToSeconds() > source_address_token_future_secs_) {
return false;
}
- if (now.IsAfter(timestamp) && delta.ToSeconds() > 86400) {
- // We allow one day into the past.
+ if (now.IsAfter(timestamp) &&
+ delta.ToSeconds() > source_address_token_lifetime_secs_) {
return false;
}
diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h
index 588d83d4..5c7d4da 100644
--- a/net/quic/crypto/crypto_server_config.h
+++ b/net/quic/crypto/crypto_server_config.h
@@ -54,14 +54,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
static const char TESTING[];
// DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for
- // using in tests. |extra_tags| contains additional key/value pairs that will
- // be inserted into the config. If |expiry_time| is non-zero then it's used
- // as the expiry for the server config in UNIX epoch seconds. Otherwise the
- // default expiry time is six months from now.
+ // using in tests. If |expiry_time| is non-zero then it's used as the expiry
+ // for the server config in UNIX epoch seconds. Otherwise the default expiry
+ // time is six months from now.
static QuicServerConfigProtobuf* DefaultConfig(
QuicRandom* rand,
const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags,
uint64 expiry_time);
// AddConfig adds a QuicServerConfigProtobuf to the availible configurations.
@@ -69,12 +67,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// takes ownership of the CryptoHandshakeMessage.
CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf);
- // AddDefaultConfig creates a config and then calls AddConfig to add it. See
- // the comment for |DefaultConfig| for details of the arguments.
+ // AddDefaultConfig calls DefaultConfig to create a config and then calls
+ // AddConfig to add it. See the comment for |DefaultConfig| for details of
+ // the arguments.
CryptoHandshakeMessage* AddDefaultConfig(
QuicRandom* rand,
const QuicClock* clock,
- const CryptoHandshakeMessage& extra_tags,
uint64 expiry_time);
// ProcessClientHello processes |client_hello| and decides whether to accept
@@ -97,7 +95,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
QuicGuid guid,
const IPEndPoint& client_ip,
- const QuicClock* now,
+ const QuicClock* clock,
QuicRandom* rand,
QuicCryptoNegotiatedParameters* params,
CryptoHandshakeMessage* out,
@@ -113,6 +111,27 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// per-connection.
void SetEphemeralKeySource(EphemeralKeySource* ephemeral_key_source);
+ // 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.
+ void set_strike_register_max_entries(uint32 max_entries);
+
+ // set_strike_register_window_secs sets the number of seconds around the
+ // current time that the strike register will attempt to be authoritative
+ // for. Setting a larger value allows for greater client clock-skew, but
+ // means that the quiescent startup period must be longer.
+ void set_strike_register_window_secs(uint32 window_secs);
+
+ // set_source_address_token_future_secs sets the number of seconds into the
+ // future that source-address tokens will be accepted from. Since
+ // source-address tokens are authenticated, this should only happen if
+ // another, valid server has clock-skew.
+ void set_source_address_token_future_secs(uint32 future_secs);
+
+ // set_source_address_token_lifetime_secs sets the number of seconds that a
+ // source-address token will be valid for.
+ void set_source_address_token_lifetime_secs(uint32 lifetime_secs);
+
private:
friend class test::QuicCryptoServerConfigPeer;
@@ -177,6 +196,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// ephemeral_key_source_ contains an object that caches ephemeral keys for a
// short period of time.
scoped_ptr<EphemeralKeySource> ephemeral_key_source_;
+
+ // These fields store configuration values. See the comments for their
+ // respective setter functions.
+ uint32 strike_register_max_entries_;
+ uint32 strike_register_window_secs_;
+ uint32 source_address_token_future_secs_;
+ uint32 source_address_token_lifetime_secs_;
};
} // namespace net
diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc
index bf1f4d2..a95ac42 100644
--- a/net/quic/crypto/crypto_utils.cc
+++ b/net/quic/crypto/crypto_utils.cc
@@ -17,53 +17,6 @@ using std::string;
namespace net {
-// static
-bool CryptoUtils::FindMutualTag(const QuicTagVector& our_tags_vector,
- const QuicTag* their_tags,
- size_t num_their_tags,
- Priority priority,
- QuicTag* out_result,
- size_t* out_index) {
- if (our_tags_vector.empty()) {
- return false;
- }
- const size_t num_our_tags = our_tags_vector.size();
- const QuicTag* our_tags = &our_tags_vector[0];
-
- size_t num_priority_tags, num_inferior_tags;
- const QuicTag* priority_tags;
- const QuicTag* inferior_tags;
- if (priority == LOCAL_PRIORITY) {
- num_priority_tags = num_our_tags;
- priority_tags = our_tags;
- num_inferior_tags = num_their_tags;
- inferior_tags = their_tags;
- } else {
- num_priority_tags = num_their_tags;
- priority_tags = their_tags;
- num_inferior_tags = num_our_tags;
- inferior_tags = our_tags;
- }
-
- for (size_t i = 0; i < num_priority_tags; i++) {
- for (size_t j = 0; j < num_inferior_tags; j++) {
- if (priority_tags[i] == inferior_tags[j]) {
- *out_result = priority_tags[i];
- if (out_index) {
- if (priority == LOCAL_PRIORITY) {
- *out_index = j;
- } else {
- *out_index = i;
- }
- }
- return true;
- }
- }
- }
-
- return false;
-}
-
void CryptoUtils::GenerateNonce(QuicWallTime now,
QuicRandom* random_generator,
StringPiece orbit,
diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h
index 6c607a5..e516718 100644
--- a/net/quic/crypto/crypto_utils.h
+++ b/net/quic/crypto/crypto_utils.h
@@ -23,31 +23,11 @@ struct QuicCryptoNegotiatedParameters;
class NET_EXPORT_PRIVATE CryptoUtils {
public:
- enum Priority {
- LOCAL_PRIORITY,
- PEER_PRIORITY,
- };
-
enum Perspective {
SERVER,
CLIENT,
};
- // FindMutualTag sets |out_result| to the first tag in the priority list that
- // is also in the other list and returns true. If there is no intersection it
- // returns false.
- //
- // Which list has priority is determined by |priority|.
- //
- // If |out_index| is non-NULL and a match is found then the index of that
- // match in |their_tags| is written to |out_index|.
- static bool FindMutualTag(const QuicTagVector& our_tags,
- const QuicTag* their_tags,
- size_t num_their_tags,
- Priority priority,
- QuicTag* out_result,
- size_t* out_index);
-
// Generates the connection nonce. The nonce is formed as:
// <4 bytes> current time
// <8 bytes> |orbit| (or random if |orbit| is empty)
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index 30ca803..796fef6 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -24,9 +24,10 @@ QuicClientSession::QuicClientSession(
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
const string& server_hostname,
+ const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
NetLog* net_log)
- : QuicSession(connection, false),
+ : QuicSession(connection, config, false),
weak_factory_(this),
stream_factory_(stream_factory),
socket_(socket),
@@ -35,13 +36,12 @@ QuicClientSession::QuicClientSession(
num_total_streams_(0),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
logger_(net_log_) {
- config_.SetDefaults();
+ QuicSession::config()->SetDefaults();
crypto_stream_.reset(
crypto_client_stream_factory ?
crypto_client_stream_factory->CreateQuicCryptoClientStream(
- server_hostname, config_, this, crypto_config) :
- new QuicCryptoClientStream(
- server_hostname, config_, this, crypto_config));
+ server_hostname, this, crypto_config) :
+ new QuicCryptoClientStream(server_hostname, this, crypto_config));
connection->set_debug_visitor(&logger_);
// TODO(rch): pass in full host port proxy pair
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index fbbbecf..2a63ca1 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -26,6 +26,10 @@ class QuicConnectionHelper;
class QuicCryptoClientStreamFactory;
class QuicStreamFactory;
+namespace test {
+class QuicClientSessionPeer;
+} // namespace test
+
class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
public:
// Constructs a new session which will own |connection| and |helper|, but
@@ -36,6 +40,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
const std::string& server_hostname,
+ const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
NetLog* net_log);
@@ -70,13 +75,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
QuicStreamId id) OVERRIDE;
private:
+ friend class test::QuicClientSessionPeer;
// A completion callback invoked when a read completes.
void OnReadComplete(int result);
base::WeakPtrFactory<QuicClientSession> weak_factory_;
- // config_ contains non-crypto configuration options negotiated in the crypto
- // handshake.
- QuicConfig config_;
scoped_ptr<QuicCryptoClientStream> crypto_stream_;
QuicStreamFactory* stream_factory_;
scoped_ptr<DatagramClientSocket> socket_;
diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc
index 3883a05..cab44a7 100644
--- a/net/quic/quic_client_session_test.cc
+++ b/net/quic/quic_client_session_test.cc
@@ -13,6 +13,7 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_client_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
using testing::_;
@@ -29,8 +30,9 @@ class QuicClientSessionTest : public ::testing::Test {
: guid_(1),
connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)),
session_(connection_, NULL, NULL, NULL, kServerHostname,
- &crypto_config_, &net_log_) {
+ QuicConfig(), &crypto_config_, &net_log_) {
crypto_config_.SetDefaults();
+ QuicClientSessionPeer::SetMaxOpenStreams(&session_, 1, 1);
}
void CompleteCryptoHandshake() {
@@ -49,7 +51,6 @@ class QuicClientSessionTest : public ::testing::Test {
MockRandom random_;
QuicConnectionVisitorInterface* visitor_;
TestCompletionCallback callback_;
- QuicConfig* config_;
QuicCryptoClientConfig crypto_config_;
};
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
index 867cb6d..eacde84 100644
--- a/net/quic/quic_config.cc
+++ b/net/quic/quic_config.cc
@@ -4,126 +4,340 @@
#include "net/quic/quic_config.h"
+#include <algorithm>
+
+#include "base/logging.h"
+
using std::string;
namespace net {
-QuicNegotiatedParameters::QuicNegotiatedParameters()
- : idle_connection_state_lifetime(QuicTime::Delta::Zero()),
- keepalive_timeout(QuicTime::Delta::Zero()) {
+QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence)
+ : tag_(tag),
+ presence_(presence),
+ negotiated_(false) {
}
-QuicConfig::QuicConfig()
- : idle_connection_state_lifetime_(QuicTime::Delta::Zero()),
- keepalive_timeout_(QuicTime::Delta::Zero()) {
+QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence)
+ : QuicNegotiableValue(tag, presence) {
}
-QuicConfig::~QuicConfig() {
+void QuicNegotiableUint32::set(uint32 max, uint32 default_value) {
+ DCHECK_LE(default_value, max);
+ max_value_ = max;
+ default_value_ = default_value;
}
-void QuicConfig::SetDefaults() {
- idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(300);
- keepalive_timeout_ = QuicTime::Delta::Zero();
- congestion_control_.clear();
- congestion_control_.push_back(kQBIC);
+uint32 QuicNegotiableUint32::GetUint32() const {
+ if (negotiated_) {
+ return negotiated_value_;
+ }
+ return default_value_;
+}
+
+void QuicNegotiableUint32::ToHandshakeMessage(
+ CryptoHandshakeMessage* out) const {
+ if (negotiated_) {
+ out->SetValue(tag_, negotiated_value_);
+ } else {
+ out->SetValue(tag_, max_value_);
+ }
}
-bool QuicConfig::SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg) {
- const QuicTag* cgst;
- size_t num_cgst;
- QuicErrorCode error;
+QuicErrorCode QuicNegotiableUint32::ReadUint32(
+ const CryptoHandshakeMessage& msg,
+ uint32* out,
+ string* error_details) const {
+ DCHECK(error_details != NULL);
+ QuicErrorCode error = msg.GetUint32(tag_, out);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out = default_value_;
- error = scfg.GetTaglist(kCGST, &cgst, &num_cgst);
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicErrorCode QuicNegotiableUint32::ProcessClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != NULL);
+ uint32 value;
+ QuicErrorCode error = ReadUint32(client_hello, &value, error_details);
if (error != QUIC_NO_ERROR) {
- return false;
+ return error;
}
- congestion_control_.assign(cgst, cgst + num_cgst);
+ negotiated_ = true;
+ negotiated_value_ = std::min(value, max_value_);
- uint32 idle;
- error = scfg.GetUint32(kICSL, &idle);
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicNegotiableUint32::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != NULL);
+ uint32 value;
+ QuicErrorCode error = ReadUint32(server_hello, &value, error_details);
if (error != QUIC_NO_ERROR) {
- return false;
+ return error;
}
- idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(idle);
-
- keepalive_timeout_ = QuicTime::Delta::Zero();
- uint32 keepalive;
- error = scfg.GetUint32(kKATO, &keepalive);
- // KATO is optional.
- if (error == QUIC_NO_ERROR) {
- keepalive_timeout_ = QuicTime::Delta::FromSeconds(keepalive);
+ if (value > max_value_) {
+ *error_details = "Invalid value received for " +
+ QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
}
- return true;
+ negotiated_ = true;
+ negotiated_value_ = value;
+ return QUIC_NO_ERROR;
}
-void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
- out->SetValue(
- kICSL, static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()));
- out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout_.ToSeconds()));
- out->SetVector(kCGST, congestion_control_);
+QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence)
+ : QuicNegotiableValue(tag, presence) {
+}
+
+QuicNegotiableTag::~QuicNegotiableTag() {}
+
+void QuicNegotiableTag::set(const QuicTagVector& possible,
+ QuicTag default_value) {
+ DCHECK(std::find(possible.begin(), possible.end(), default_value) !=
+ possible.end());
+ possible_values_ = possible;
+ default_value_ = default_value;
+}
+
+QuicTag QuicNegotiableTag::GetTag() const {
+ if (negotiated_) {
+ return negotiated_tag_;
+ }
+ return default_value_;
}
-QuicErrorCode QuicConfig::ProcessFinalPeerHandshake(
+void QuicNegotiableTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (negotiated_) {
+ // Because of the way we serialize and parse handshake messages we can
+ // serialize this as value and still parse it as a vector.
+ out->SetValue(tag_, negotiated_tag_);
+ } else {
+ out->SetVector(tag_, possible_values_);
+ }
+}
+
+QuicErrorCode QuicNegotiableTag::ReadVector(
const CryptoHandshakeMessage& msg,
- CryptoUtils::Priority priority,
- QuicNegotiatedParameters* out_params,
+ const QuicTag** out,
+ size_t* out_length,
string* error_details) const {
DCHECK(error_details != NULL);
+ QuicErrorCode error = msg.GetTaglist(tag_, out, out_length);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out_length = 1;
+ *out = &default_value_;
- const QuicTag* their_congestion_controls;
- size_t num_their_congestion_controls;
- QuicErrorCode error;
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
- error = msg.GetTaglist(kCGST, &their_congestion_controls,
- &num_their_congestion_controls);
+QuicErrorCode QuicNegotiableTag::ProcessClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != NULL);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error = ReadVector(client_hello, &received_tags,
+ &received_tags_length, error_details);
if (error != QUIC_NO_ERROR) {
- *error_details = "Missing CGST";
return error;
}
- if (!CryptoUtils::FindMutualTag(congestion_control_,
- their_congestion_controls,
- num_their_congestion_controls,
- priority,
- &out_params->congestion_control,
- NULL)) {
- *error_details = "Unsuported CGST";
+ QuicTag negotiated_tag;
+ if (!QuicUtils::FindMutualTag(possible_values_,
+ received_tags,
+ received_tags_length,
+ QuicUtils::LOCAL_PRIORITY,
+ &negotiated_tag,
+ NULL)) {
+ *error_details = "Unsuported " + QuicUtils::TagToString(tag_);
return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
}
- uint32 idle;
- error = msg.GetUint32(kICSL, &idle);
+ negotiated_ = true;
+ negotiated_tag_ = negotiated_tag;
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicNegotiableTag::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != NULL);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error = ReadVector(server_hello, &received_tags,
+ &received_tags_length, error_details);
if (error != QUIC_NO_ERROR) {
- *error_details = "Missing ICSL";
return error;
}
- out_params->idle_connection_state_lifetime = QuicTime::Delta::FromSeconds(
- std::min(static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()),
- idle));
-
- uint32 keepalive;
- error = msg.GetUint32(kKATO, &keepalive);
- switch (error) {
- case QUIC_NO_ERROR:
- out_params->keepalive_timeout = QuicTime::Delta::FromSeconds(
- std::min(static_cast<uint32>(keepalive_timeout_.ToSeconds()),
- keepalive));
- break;
- case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
- // KATO is optional.
- out_params->keepalive_timeout = QuicTime::Delta::Zero();
- break;
- default:
- *error_details = "Bad KATO";
- return error;
+ if (received_tags_length != 1 ||
+ std::find(possible_values_.begin(), possible_values_.end(),
+ *received_tags) == possible_values_.end()) {
+ *error_details = "Invalid " + QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
}
+ negotiated_ = true;
+ negotiated_tag_ = *received_tags;
return QUIC_NO_ERROR;
}
+QuicConfig::QuicConfig() :
+ congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED),
+ idle_connection_state_lifetime_seconds_(
+ kICSL, QuicNegotiableValue::PRESENCE_REQUIRED),
+ keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL),
+ max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED) {
+ idle_connection_state_lifetime_seconds_.set(0, 0);
+ keepalive_timeout_seconds_.set(0, 0);
+}
+
+QuicConfig::~QuicConfig() {}
+
+void QuicConfig::set_congestion_control(
+ const QuicTagVector& congestion_control,
+ QuicTag default_congestion_control) {
+ congestion_control_.set(congestion_control, default_congestion_control);
+}
+
+QuicTag QuicConfig::congestion_control() const {
+ return congestion_control_.GetTag();
+}
+
+void QuicConfig::set_idle_connection_state_lifetime(
+ QuicTime::Delta max_idle_connection_state_lifetime,
+ QuicTime::Delta default_idle_conection_state_lifetime) {
+ idle_connection_state_lifetime_seconds_.set(
+ max_idle_connection_state_lifetime.ToSeconds(),
+ default_idle_conection_state_lifetime.ToSeconds());
+}
+
+QuicTime::Delta QuicConfig::idle_connection_state_lifetime() const {
+ return QuicTime::Delta::FromSeconds(
+ idle_connection_state_lifetime_seconds_.GetUint32());
+}
+
+QuicTime::Delta QuicConfig::keepalive_timeout() const {
+ return QuicTime::Delta::FromSeconds(
+ keepalive_timeout_seconds_.GetUint32());
+}
+
+void QuicConfig::set_max_streams_per_connection(size_t max_streams,
+ size_t default_streams) {
+ max_streams_per_connection_.set(max_streams, default_streams);
+}
+
+uint32 QuicConfig::max_streams_per_connection() const {
+ return max_streams_per_connection_.GetUint32();
+}
+
+bool QuicConfig::negotiated() {
+ return congestion_control_.negotiated() &&
+ idle_connection_state_lifetime_seconds_.negotiated() &&
+ keepalive_timeout_seconds_.negotiated() &&
+ max_streams_per_connection_.negotiated();
+}
+
+void QuicConfig::SetDefaults() {
+ congestion_control_.set(QuicTagVector(1, kQBIC), kQBIC);
+ idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs,
+ kDefaultTimeoutSecs);
+ // kKATO is optional. Return 0 if not negotiated.
+ keepalive_timeout_seconds_.set(0, 0);
+ max_streams_per_connection_.set(kDefaultMaxStreamsPerConnection,
+ kDefaultMaxStreamsPerConnection);
+}
+
+void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ congestion_control_.ToHandshakeMessage(out);
+ idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out);
+ keepalive_timeout_seconds_.ToHandshakeMessage(out);
+ max_streams_per_connection_.ToHandshakeMessage(out);
+}
+
+QuicErrorCode QuicConfig::ProcessClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+
+ QuicErrorCode error = QUIC_NO_ERROR;
+ if (error == QUIC_NO_ERROR) {
+ error = congestion_control_.ProcessClientHello(client_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = idle_connection_state_lifetime_seconds_.ProcessClientHello(
+ client_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = keepalive_timeout_seconds_.ProcessClientHello(
+ client_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = max_streams_per_connection_.ProcessClientHello(
+ client_hello, error_details);
+ }
+ return error;
+}
+
+QuicErrorCode QuicConfig::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ string* error_details) {
+ DCHECK(error_details != NULL);
+
+ QuicErrorCode error = QUIC_NO_ERROR;
+ if (error == QUIC_NO_ERROR) {
+ error = congestion_control_.ProcessServerHello(server_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = idle_connection_state_lifetime_seconds_.ProcessServerHello(
+ server_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = keepalive_timeout_seconds_.ProcessServerHello(
+ server_hello, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = max_streams_per_connection_.ProcessServerHello(
+ server_hello, error_details);
+ }
+ return error;
+}
+
} // namespace net
diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h
index 013a4f2..0f11493 100644
--- a/net/quic/quic_config.h
+++ b/net/quic/quic_config.h
@@ -7,21 +7,126 @@
#include <string>
+#include "base/basictypes.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/quic_time.h"
+#include "net/quic/quic_utils.h"
namespace net {
-// QuicNegotiatedParameters contains non-crypto parameters that are agreed upon
-// during the crypto handshake.
-class NET_EXPORT_PRIVATE QuicNegotiatedParameters {
+class NET_EXPORT_PRIVATE QuicNegotiableValue {
public:
- QuicNegotiatedParameters();
+ enum Presence {
+ // This negotiable value can be absent from the handshake message. Default
+ // value is selected as the negotiated value in such a case.
+ PRESENCE_OPTIONAL,
+ // This negotiable value is required in the handshake message otherwise the
+ // Process*Hello function returns an error.
+ PRESENCE_REQUIRED,
+ };
- QuicTag congestion_control;
- QuicTime::Delta idle_connection_state_lifetime;
- QuicTime::Delta keepalive_timeout;
+ QuicNegotiableValue(QuicTag tag, Presence presence);
+
+ bool negotiated() const {
+ return negotiated_;
+ }
+
+ protected:
+ const QuicTag tag_;
+ const Presence presence_;
+ bool negotiated_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
+ public:
+ QuicNegotiableUint32(QuicTag name, Presence presence);
+
+ // Sets the maximum possible value that can be achieved after negotiation and
+ // also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg
+ // doesn't contain a value corresponding to |name_|. |max| is serialised via
+ // ToHandshakeMessage call if |negotiated_| is false.
+ void set(uint32 max, uint32 default_value);
+
+ // Returns the value negotiated if |negotiated_| is true, otherwise returns
+ // default_value_ (used to set default values before negotiation finishes).
+ uint32 GetUint32() const;
+
+ // Serialises |name_| and value to |out|. If |negotiated_| is true then
+ // |negotiated_value_| is serialised, otherwise |max_value_| is serialised.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+
+ // Sets |negotiated_value_| to the minimum of |max_value_| and the
+ // corresponding value from |client_hello|. If the corresponding value is
+ // missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to
+ // |default_value_|.
+ QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
+ std::string* error_details);
+
+ // Sets the |negotiated_value_| to the corresponding value from
+ // |server_hello|. Returns error if the value received in |server_hello| is
+ // greater than |max_value_|. If the corresponding value is missing and
+ // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|,
+ QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
+ std::string* error_details);
+
+ private:
+ // Reads the value corresponding to |name_| from |msg| into |out|. If the
+ // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set
+ // to |max_value_|.
+ QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
+ uint32* out,
+ std::string* error_details) const;
+
+ uint32 max_value_;
+ uint32 default_value_;
+ uint32 negotiated_value_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
+ public:
+ QuicNegotiableTag(QuicTag name, Presence presence);
+ ~QuicNegotiableTag();
+
+ // Sets the possible values that |negotiated_tag_| can take after negotiation
+ // and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO
+ // msg doesn't contain tag |name_|.
+ void set(const QuicTagVector& possible_values, QuicTag default_value);
+
+ // Returns the negotiated tag if |negotiated_| is true, otherwise returns
+ // |default_value_| (used to set default values before negotiation finishes).
+ QuicTag GetTag() const;
+
+ // Serialises |name_| and vector (either possible or negotiated) to |out|. If
+ // |negotiated_| is true then |negotiated_tag_| is serialised, otherwise
+ // |possible_values_| is serialised.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+
+ // Selects the tag common to both tags in |client_hello| for |name_| and
+ // |possible_values_| with preference to tag in |possible_values_|. The
+ // selected tag is set as |negotiated_tag_|.
+ QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
+ std::string* error_details);
+
+ // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|.
+ // Returns error if the value received in |server_hello| isn't present in
+ // |possible_values_|.
+ QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
+ std::string* error_details);
+
+ private:
+ // Reads the vector corresponding to |name_| from |msg| into |out|. If the
+ // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set
+ // to |possible_values_|.
+ QuicErrorCode ReadVector(const CryptoHandshakeMessage& msg,
+ const QuicTag** out,
+ size_t* out_length,
+ std::string* error_details) const;
+
+ QuicTag negotiated_tag_;
+ QuicTagVector possible_values_;
+ QuicTag default_value_;
};
// QuicConfig contains non-crypto configuration options that are negotiated in
@@ -31,39 +136,52 @@ class NET_EXPORT_PRIVATE QuicConfig {
QuicConfig();
~QuicConfig();
+ void set_congestion_control(const QuicTagVector& congestion_control,
+ QuicTag default_congestion_control);
+
+ QuicTag congestion_control() const;
+
void set_idle_connection_state_lifetime(
- QuicTime::Delta idle_connection_state_lifetime) {
- idle_connection_state_lifetime_ = idle_connection_state_lifetime;
- }
+ QuicTime::Delta max_idle_connection_state_lifetime,
+ QuicTime::Delta default_idle_conection_state_lifetime);
+
+ QuicTime::Delta idle_connection_state_lifetime() const;
+
+ QuicTime::Delta keepalive_timeout() const;
+
+ void set_max_streams_per_connection(size_t max_streams,
+ size_t default_streams);
+
+ uint32 max_streams_per_connection() const;
+
+ bool negotiated();
// SetDefaults sets the members to sensible, default values.
void SetDefaults();
- // SetFromMessage extracts the non-crypto configuration from |msg| and sets
- // the members of this object to match. This is expected to be called in the
- // case of a server which is loading a server config. The server config
- // contains the non-crypto parameters and so the server will need to keep its
- // QuicConfig in sync with the server config that it'll be sending to
- // clients.
- bool SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg);
-
// ToHandshakeMessage serializes the settings in this object as a series of
// tags /value pairs and adds them to |out|.
void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
- QuicErrorCode ProcessFinalPeerHandshake(
- const CryptoHandshakeMessage& peer_handshake,
- CryptoUtils::Priority priority,
- QuicNegotiatedParameters* out_params,
- std::string* error_details) const;
+ // Calls ProcessClientHello on each negotiable parameter. On failure returns
+ // the corresponding QuicErrorCode and sets detailed error in |error_details|.
+ QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
+ std::string* error_details);
+
+ // Calls ProcessServerHello on each negotiable parameter. On failure returns
+ // the corresponding QuicErrorCode and sets detailed error in |error_details|.
+ QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
+ std::string* error_details);
private:
// Congestion control feedback type.
- QuicTagVector congestion_control_;
+ QuicNegotiableTag congestion_control_;
// Idle connection state lifetime
- QuicTime::Delta idle_connection_state_lifetime_;
+ QuicNegotiableUint32 idle_connection_state_lifetime_seconds_;
// Keepalive timeout, or 0 to turn off keepalive probes
- QuicTime::Delta keepalive_timeout_;
+ QuicNegotiableUint32 keepalive_timeout_seconds_;
+ // Maximum number of streams that the connection can support.
+ QuicNegotiableUint32 max_streams_per_connection_;
};
} // namespace net
diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc
new file mode 100644
index 0000000..eeaa97d
--- /dev/null
+++ b/net/quic/quic_config_test.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 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_config.h"
+
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class QuicConfigTest : public ::testing::Test {
+ protected:
+ QuicConfigTest() {
+ config_.SetDefaults();
+ }
+
+ QuicConfig config_;
+};
+
+TEST_F(QuicConfigTest, ToHandshakeMessage) {
+ config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5),
+ QuicTime::Delta::FromSeconds(2));
+ config_.set_max_streams_per_connection(4, 2);
+ CryptoHandshakeMessage msg;
+ config_.ToHandshakeMessage(&msg);
+
+ uint32 value;
+ QuicErrorCode error = msg.GetUint32(kICSL, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(5u, value);
+
+ error = msg.GetUint32(kMSPC, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(4u, value);
+
+ const QuicTag* out;
+ size_t out_len;
+ error = msg.GetTaglist(kCGST, &out, &out_len);
+ EXPECT_EQ(1u, out_len);
+ EXPECT_EQ(kQBIC, *out);
+}
+
+TEST_F(QuicConfigTest, ProcessClientHello) {
+ QuicConfig client_config;
+ QuicTagVector cgst;
+ cgst.push_back(kINAR);
+ cgst.push_back(kQBIC);
+ client_config.set_congestion_control(cgst, kQBIC);
+ client_config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs));
+ client_config.set_max_streams_per_connection(
+ 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
+
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs),
+ config_.idle_connection_state_lifetime());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ config_.max_streams_per_connection());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
+}
+
+TEST_F(QuicConfigTest, ProcessServerHello) {
+ QuicConfig server_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ server_config.set_congestion_control(cgst, kQBIC);
+ server_config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
+ QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2));
+ server_config.set_max_streams_per_connection(
+ kDefaultMaxStreamsPerConnection / 2,
+ kDefaultMaxStreamsPerConnection / 2);
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(kQBIC, config_.congestion_control());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2),
+ config_.idle_connection_state_lifetime());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2,
+ config_.max_streams_per_connection());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout());
+}
+
+TEST_F(QuicConfigTest, MissingValueInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ // Missing kMSPC. KATO is optional.
+ string error_details;
+ const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, MissingValueInSHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetValue(kMSPC, 3);
+ // Missing CGST. KATO is optional.
+ string error_details;
+ const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, OutOfBoundSHLO) {
+ QuicConfig server_config;
+ server_config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs),
+ QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs));
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
+}
+
+TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) {
+ QuicConfig server_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ cgst.push_back(kINAR);
+ server_config.set_congestion_control(cgst, kQBIC);
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details);
+ EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
+}
+
+TEST_F(QuicConfigTest, NoOverLapInCGST) {
+ QuicConfig server_config;
+ server_config.SetDefaults();
+ QuicTagVector cgst;
+ cgst.push_back(kINAR);
+ server_config.set_congestion_control(cgst, kINAR);
+
+ CryptoHandshakeMessage msg;
+ string error_details;
+ server_config.ToHandshakeMessage(&msg);
+ const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details);
+ LOG(INFO) << QuicUtils::ErrorToString(error);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 481fca9..51319ff 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -30,17 +30,6 @@ namespace {
// This will likely have to be tuned.
const QuicPacketSequenceNumber kMaxPacketGap = 5000;
-// The maximum number of nacks which can be transmitted in a single ack packet
-// without exceeding kMaxPacketSize.
-// TODO(satyamshekhar): Get rid of magic numbers and move this to protocol.h
-// 16 - Min ack frame size.
-// 16 - Crypto hash for integrity. Not a static value. Use
-// QuicEncrypter::GetMaxPlaintextSize.
-size_t GetMaxUnackedPackets(bool include_version) {
- return (kMaxPacketSize - GetPacketHeaderSize(include_version) - 16 - 16) /
- kSequenceNumberSize;
-}
-
// We want to make sure if we get a large nack packet, we don't queue up too
// many packets at once. 10 is arbitrary.
const int kMaxRetransmissionsPerAck = 10;
@@ -58,7 +47,11 @@ const int kMaxPacketsToSerializeAtOnce = 6;
// Limit the number of packets we send per retransmission-alarm so we
// eventually cede. 10 is arbitrary.
-const int kMaxPacketsPerRetransmissionAlarm = 10;
+const size_t kMaxPacketsPerRetransmissionAlarm = 10;
+
+// Limit the number of FEC groups to two. If we get enough out of order packets
+// that this becomes limiting, we can revisit.
+const size_t kMaxFecGroups = 2;
bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a;
@@ -91,20 +84,20 @@ QuicConnection::QuicConnection(QuicGuid guid,
debug_visitor_(NULL),
packet_creator_(guid_, &framer_, random_generator_, is_server),
packet_generator_(this, &packet_creator_),
- timeout_(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)),
+ timeout_(QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)),
time_of_last_received_packet_(clock_->ApproximateNow()),
time_of_last_sent_packet_(clock_->ApproximateNow()),
time_largest_observed_(QuicTime::Zero()),
congestion_manager_(clock_, kTCP),
version_negotiation_state_(START_NEGOTIATION),
quic_version_(kQuicVersion1),
+ max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm),
is_server_(is_server),
connected_(true),
received_truncated_ack_(false),
- send_ack_in_response_to_packet_(false) {
+ send_ack_in_response_to_packet_(false),
+ address_migrating_(false) {
helper_->SetConnection(this);
- // TODO(satyamshekhar): Have a smaller timeout till version is negotiated and
- // connection is established (CHLO fully processed).
helper_->SetTimeoutAlarm(timeout_);
framer_.set_visitor(this);
framer_.set_entropy_calculator(&entropy_manager_);
@@ -160,10 +153,6 @@ void QuicConnection::OnPacket() {
time_of_last_received_packet_ = clock_->Now();
DVLOG(1) << "time of last received packet: "
<< time_of_last_received_packet_.ToDebuggingValue();
-
- // TODO(alyssar, rch) handle migration!
- self_address_ = last_self_address_;
- peer_address_ = last_peer_address_;
}
void QuicConnection::OnPublicResetPacket(
@@ -175,6 +164,12 @@ void QuicConnection::OnPublicResetPacket(
}
bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) {
+ if (address_migrating_) {
+ SendConnectionCloseWithDetails(
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ "Address migration is not yet a supported feature");
+ }
+
// TODO(satyamshekhar): Implement no server state in this mode.
if (!is_server_) {
LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. "
@@ -226,6 +221,11 @@ bool QuicConnection::OnProtocolVersionMismatch(QuicTag received_version) {
// Handles version negotiation for client connection.
void QuicConnection::OnVersionNegotiationPacket(
const QuicVersionNegotiationPacket& packet) {
+ if (address_migrating_) {
+ SendConnectionCloseWithDetails(
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ "Address migration is not yet a supported feature");
+ }
if (is_server_) {
LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server."
<< "Closing connection.";
@@ -268,6 +268,13 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
debug_visitor_->OnPacketHeader(header);
}
+ if (address_migrating_) {
+ SendConnectionCloseWithDetails(
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ "Address migration is not yet a supported feature");
+ return false;
+ }
+
// Will be decrement below if we fall through to return true;
++stats_.packets_dropped;
@@ -281,7 +288,8 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
last_header_.packet_sequence_number)) {
DLOG(INFO) << ENDPOINT << "Packet " << header.packet_sequence_number
<< " out of bounds. Discarding";
- // TODO(alyssar) close the connection entirely.
+ SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER,
+ "Packet sequence number out of bounds");
return false;
}
@@ -326,7 +334,9 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
void QuicConnection::OnFecProtectedPayload(StringPiece payload) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
- group->Update(last_header_, payload);
+ if (group != NULL) {
+ group->Update(last_header_, payload);
+ }
}
bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
@@ -356,16 +366,9 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
return false;
}
- // TODO(satyamshekhar): Not true if missing_packets.size() was actually
- // kMaxUnackedPackets. This can result in a dead connection if all the
- // missing packets get lost during retransmission. Now the new packets(or the
- // older packets) will not be retransmitted due to RTO
- // since received_truncated_ack_ is true and their sequence_number is >
- // peer_largest_observed_packet. Fix either by resetting it in
- // MaybeRetransmitPacketForRTO or keeping an explicit flag for ack truncation.
received_truncated_ack_ =
incoming_ack.received_info.missing_packets.size() >=
- GetMaxUnackedPackets(last_header_.public_header.version_flag);
+ QuicFramer::GetMaxUnackedPackets(last_header_.public_header.version_flag);
UpdatePacketInformationReceivedByPeer(incoming_ack);
UpdatePacketInformationSentByPeer(incoming_ack);
@@ -422,7 +425,8 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
// We can't have too many unacked packets, or our ack frames go over
// kMaxPacketSize.
DCHECK_LE(incoming_ack.received_info.missing_packets.size(),
- GetMaxUnackedPackets(last_header_.public_header.version_flag));
+ QuicFramer::GetMaxUnackedPackets(
+ last_header_.public_header.version_flag));
if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) {
DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
@@ -581,8 +585,10 @@ void QuicConnection::UpdatePacketInformationSentByPeer(
void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_NE(0u, last_header_.fec_group);
QuicFecGroup* group = GetFecGroup();
- group->UpdateFec(last_header_.packet_sequence_number,
- last_header_.fec_entropy_flag, fec);
+ if (group != NULL) {
+ group->UpdateFec(last_header_.packet_sequence_number,
+ last_header_.fec_entropy_flag, fec);
+ }
}
bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
@@ -618,8 +624,12 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
}
void QuicConnection::OnPacketComplete() {
- // TODO(satyamshekhar): Don't do anything if this packet closed the
- // connection.
+ // Don't do anything if this packet closed the connection.
+ if (!connected_) {
+ last_stream_frames_.clear();
+ return;
+ }
+
if (!last_packet_revived_) {
DLOG(INFO) << ENDPOINT << "Got packet "
<< last_header_.packet_sequence_number
@@ -717,8 +727,19 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
}
last_packet_revived_ = false;
last_size_ = packet.length();
- last_self_address_ = self_address;
- last_peer_address_ = peer_address;
+
+ address_migrating_ = false;
+
+ if (peer_address_.address().empty()) {
+ peer_address_ = peer_address;
+ }
+ if (self_address_.address().empty()) {
+ self_address_ = self_address;
+ }
+
+ if (!(peer_address == peer_address_) && (self_address == self_address_)) {
+ address_migrating_ = true;
+ }
stats_.bytes_received += packet.length();
++stats_.packets_received;
@@ -826,12 +847,18 @@ bool QuicConnection::MaybeRetransmitPacketForRTO(
return true;
}
+ RetransmissionMap::iterator retransmission_it =
+ retransmission_map_.find(sequence_number);
// If the packet hasn't been acked and we're getting truncated acks, ignore
// any RTO for packets larger than the peer's largest observed packet; it may
// have been received by the peer and just wasn't acked due to the ack frame
// running out of space.
if (received_truncated_ack_ &&
- sequence_number > peer_largest_observed_packet_) {
+ sequence_number > peer_largest_observed_packet_ &&
+ // We allow retransmission of already retransmitted packets so that we
+ // retransmit packets that were retransmissions of the packet with
+ // sequence number < the largest observed field of the truncated ack.
+ retransmission_it->second.number_retransmissions == 0) {
return false;
} else {
++stats_.rto_count;
@@ -901,8 +928,7 @@ bool QuicConnection::CanWrite(Retransmission retransmission,
QuicTime::Delta delay = congestion_manager_.TimeUntilSend(
now, retransmission, retransmittable);
if (delay.IsInfinite()) {
- // TODO(pwestin): should be false but trigger other bugs see b/8350327.
- return true;
+ return false;
}
// If the scheduler requires a delay, then we can not send this packet now.
@@ -983,20 +1009,23 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
int error;
QuicTime now = clock_->Now();
- int rv = helper_->WritePacketToWire(*encrypted, &error);
- if (rv == -1 && helper_->IsWriteBlocked(error)) {
- // TODO(satyashekhar): It might be more efficient (fewer system calls), if
- // all connections share this variable i.e this becomes a part of
- // PacketWriterInterface.
- write_blocked_ = true;
- // If the socket buffers the the data, then the packet should not
- // be queued and sent again, which would result in an unnecessary duplicate
- // packet being sent.
- return helper_->IsWriteBlockedDataBuffered();
+ if (helper_->WritePacketToWire(*encrypted, &error) == -1) {
+ if (helper_->IsWriteBlocked(error)) {
+ // TODO(satyashekhar): It might be more efficient (fewer system calls), if
+ // all connections share this variable i.e this becomes a part of
+ // PacketWriterInterface.
+ write_blocked_ = true;
+ // If the socket buffers the the data, then the packet should not
+ // be queued and sent again, which would result in an unnecessary
+ // duplicate packet being sent.
+ return helper_->IsWriteBlockedDataBuffered();
+ }
+ // We can't send an error as the socket is presumably borked.
+ CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
+ return false;
}
time_of_last_sent_packet_ = now;
DVLOG(1) << "time of last sent packet: " << now.ToDebuggingValue();
- // TODO(wtc): Is it correct to continue if the write failed.
// Set the retransmit alarm only when we have sent the packet to the client
// and not when it goes to the pending queue, otherwise we will end up adding
@@ -1113,7 +1142,7 @@ QuicTime QuicConnection::OnRetransmissionTimeout() {
// want to set it to the RTO of B when we return from this function.
handling_retransmission_timeout_ = true;
- for (int i = 0; i < kMaxPacketsPerRetransmissionAlarm &&
+ for (size_t i = 0; i < max_packets_per_retransmission_alarm_ &&
!retransmission_timeouts_.empty(); ++i) {
RetransmissionInfo retransmission_info = retransmission_timeouts_.top();
DCHECK(retransmission_info.scheduled_time.IsInitialized());
@@ -1206,7 +1235,16 @@ QuicFecGroup* QuicConnection::GetFecGroup() {
return NULL;
}
if (group_map_.count(fec_group_num) == 0) {
- // TODO(rch): limit the number of active FEC groups.
+ if (group_map_.size() >= kMaxFecGroups) { // Too many groups
+ if (fec_group_num < group_map_.begin()->first) {
+ // The group being requested is a group we've seen before and deleted.
+ // Don't recreate it.
+ return NULL;
+ }
+ // Clear the lowest group number.
+ delete group_map_.begin()->second;
+ group_map_.erase(group_map_.begin());
+ }
group_map_[fec_group_num] = new QuicFecGroup();
}
return group_map_[fec_group_num];
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index ce3a263..dab64ee 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -503,7 +503,8 @@ class NET_EXPORT_PRIVATE QuicConnection
void MaybeSendAckInResponseToPacket();
- // Get the FEC group associate with the last processed packet.
+ // Get the FEC group associate with the last processed packet or NULL, if the
+ // group has already been deleted.
QuicFecGroup* GetFecGroup();
// Closes any FEC groups protecting packets before |sequence_number|.
@@ -519,10 +520,6 @@ class NET_EXPORT_PRIVATE QuicConnection
// client.
IPEndPoint self_address_;
IPEndPoint peer_address_;
- // Address on the last(currently being processed) packet received. Not
- // verified/authenticated.
- IPEndPoint last_self_address_;
- IPEndPoint last_peer_address_;
bool last_packet_revived_; // True if the last packet was revived from FEC.
size_t last_size_; // Size of the last received packet.
@@ -606,6 +603,8 @@ class NET_EXPORT_PRIVATE QuicConnection
// The version of the protocol this connection is using.
QuicTag quic_version_;
+ size_t max_packets_per_retransmission_alarm_;
+
// Tracks if the connection was created by the server.
bool is_server_;
@@ -619,6 +618,10 @@ class NET_EXPORT_PRIVATE QuicConnection
bool send_ack_in_response_to_packet_;
+ // Set to true if the udp packet headers have a new self or peer address.
+ // This is checked later on validating a data or version negotiation packet.
+ bool address_migrating_;
+
DISALLOW_COPY_AND_ASSIGN(QuicConnection);
};
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
index 0c81722..fa7484a 100644
--- a/net/quic/quic_connection_helper_test.cc
+++ b/net/quic/quic_connection_helper_test.cc
@@ -23,7 +23,7 @@ namespace net {
namespace test {
const char kData[] = "foo";
-const bool kHasData = true;
+const bool kFromPeer = true;
class TestConnection : public QuicConnection {
public:
@@ -328,7 +328,7 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
// Verify that a single task was posted.
ASSERT_EQ(1u, runner_->GetPostedTasks().size());
- EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs),
+ EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultInitialTimeoutSecs),
runner_->GetPostedTasks().front().delay);
EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
@@ -336,8 +336,8 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(
- QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)),
+ EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(
+ kDefaultInitialTimeoutSecs)),
clock_.ApproximateNow());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
@@ -374,7 +374,8 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_->connected());
QuicTime start = clock_.ApproximateNow();
- // When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
+ // When we send a packet, the timeout will change to 5000 +
+ // kDefaultInitialTimeoutSecs.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000));
EXPECT_EQ(5000u, clock_.ApproximateNow().Subtract(start).ToMicroseconds());
EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
@@ -386,17 +387,18 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// network event at t=5000. The alarm will reregister.
runner_->RunNextTask();
- EXPECT_EQ(QuicTime::Zero().Add(
- QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)),
+ EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(
+ kDefaultInitialTimeoutSecs)),
clock_.ApproximateNow());
EXPECT_TRUE(connection_->connected());
// This time, we should time out.
- EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION));
runner_->RunNextTask();
- EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.ApproximateNow().Subtract(
- QuicTime::Zero()).ToMicroseconds());
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
+ clock_.ApproximateNow().Subtract(
+ QuicTime::Zero()).ToMicroseconds());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
}
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index 826681a..b761aaf 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -756,10 +756,8 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
// Call ProcessDataPacket rather than ProcessPacket, as we should not get a
// packet call to the visitor.
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false));
ProcessDataPacket(6000, 0, !kEntropyFlag);
-
- SendAckPacketToPeer(); // Packet 2
- EXPECT_EQ(0u, outgoing_ack()->received_info.largest_observed);
}
TEST_F(QuicConnectionTest, TruncatedAck) {
@@ -1119,6 +1117,45 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
ProcessAckPacket(&frame);
}
+TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
+ for (int i = 0; i < 200; ++i) {
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ connection_.SendStreamData(1, "foo", i * 3, !kFin);
+ }
+
+ // Make a truncated ack frame.
+ QuicAckFrame frame(0, QuicTime::Zero(), 1);
+ frame.received_info.largest_observed = 192;
+ InsertMissingPacketsBetween(&frame.received_info, 1, 192);
+ frame.received_info.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^
+ QuicConnectionPeer::GetSentEntropyHash(&connection_, 191);
+
+
+ EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
+ EXPECT_CALL(visitor_, OnAck(_)).Times(1);
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_));
+
+ QuicConnectionPeer::SetMaxPacketsPerRetransmissionAlarm(&connection_, 200);
+ const QuicTime::Delta kDefaultRetransmissionTime =
+ QuicTime::Delta::FromMilliseconds(500);
+ clock_.AdvanceTime(kDefaultRetransmissionTime);
+ // Only packets that are less than largest observed should be retransmitted.
+ EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191);
+ connection_.OnRetransmissionTimeout();
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
+ 2 * kDefaultRetransmissionTime.ToMicroseconds()));
+ // Retransmit already retransmitted packets event though the sequence number
+ // greater than the largest observed.
+ EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191);
+ connection_.OnRetransmissionTimeout();
+}
+
TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
@@ -1487,11 +1524,12 @@ TEST_F(QuicConnectionTest, InitialTimeout) {
EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
QuicTime default_timeout = clock_.ApproximateNow().Add(
- QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs));
+ QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs));
EXPECT_EQ(default_timeout, helper_->timeout_alarm());
// Simulate the timeout alarm firing
- clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs));
+ clock_.AdvanceTime(
+ QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs));
EXPECT_TRUE(connection_.CheckForTimeout());
EXPECT_FALSE(connection_.connected());
}
@@ -1500,9 +1538,10 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
QuicTime default_timeout = clock_.ApproximateNow().Add(
- QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs));
+ QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs));
- // When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
+ // When we send a packet, the timeout will change to 5000 +
+ // kDefaultInitialTimeoutSecs.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Send an ack so we don't set the retransimission alarm.
@@ -1512,7 +1551,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
// The original alarm will fire. We should not time out because we had a
// network event at t=5000. The alarm will reregister.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
- kDefaultTimeoutUs - 5000));
+ kDefaultInitialTimeoutSecs * 1000000 - 5000));
EXPECT_EQ(default_timeout, clock_.ApproximateNow());
EXPECT_FALSE(connection_.CheckForTimeout());
EXPECT_TRUE(connection_.connected());
@@ -1993,6 +2032,60 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) {
EXPECT_EQ(1u, stats.packets_dropped);
}
+TEST_F(QuicConnectionTest, TestFecGroupLimits) {
+ // Create and return a group for 1
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL);
+
+ // Create and return a group for 2
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL);
+
+ // Create and return a group for 4. This should remove 1 but not 2.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) == NULL);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL);
+
+ // Create and return a group for 3. This will kill off 2.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) != NULL);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) == NULL);
+
+ // Verify that adding 5 kills off 3, despite 4 being created before 3.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 5) != NULL);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL);
+}
+
+TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) {
+ // Construct a packet with stream frame and connection close frame.
+ header_.public_header.guid = guid_;
+ header_.packet_sequence_number = 1;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.fec_entropy_flag = false;
+ header_.fec_group = 0;
+
+ QuicConnectionCloseFrame qccf;
+ qccf.error_code = QUIC_PEER_GOING_AWAY;
+ qccf.ack_frame = QuicAckFrame(0, QuicTime::Zero(), 1);
+ QuicFrame close_frame(&qccf);
+ QuicFrame stream_frame(&frame1_);
+
+ QuicFrames frames;
+ frames.push_back(stream_frame);
+ frames.push_back(close_frame);
+ scoped_ptr<QuicPacket> packet(
+ framer_.ConstructFrameDataPacket(header_, frames).packet);
+ EXPECT_TRUE(NULL != packet.get());
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ ENCRYPTION_NONE, 1, *packet));
+
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true));
+ EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(0);
+
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
index e6322a8..80d298e 100644
--- a/net/quic/quic_crypto_client_stream.cc
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -15,13 +15,11 @@ namespace net {
QuicCryptoClientStream::QuicCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config)
: QuicCryptoStream(session),
next_state_(STATE_IDLE),
num_client_hellos_(0),
- config_(config),
crypto_config_(crypto_config),
server_hostname_(server_hostname) {
}
@@ -82,8 +80,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
SendHandshakeMessage(out);
return;
}
- const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
- config_.ToHandshakeMessage(&out);
+ session()->config()->ToHandshakeMessage(&out);
error = crypto_config_->FillClientHello(
server_hostname_,
session()->connection()->guid(),
@@ -97,13 +94,6 @@ void QuicCryptoClientStream::DoHandshakeLoop(
CloseConnectionWithDetails(error, error_details);
return;
}
- error = config_.ProcessFinalPeerHandshake(
- *scfg, CryptoUtils::PEER_PRIORITY, &negotiated_params_,
- &error_details);
- if (error != QUIC_NO_ERROR) {
- CloseConnectionWithDetails(error, error_details);
- return;
- }
next_state_ = STATE_RECV_SHLO;
DLOG(INFO) << "Client Sending: " << out.DebugString();
SendHandshakeMessage(out);
@@ -214,6 +204,12 @@ void QuicCryptoClientStream::DoHandshakeLoop(
error, "Server hello invalid: " + error_details);
return;
}
+ error = session()->config()->ProcessServerHello(*in, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(
+ error, "Server hello invalid: " + error_details);
+ return;
+ }
CrypterPair* crypters =
&crypto_negotiated_params_.forward_secure_crypters;
// TODO(agl): we don't currently latch this decrypter because the idea
diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h
index 041d11c..e85a764 100644
--- a/net/quic/quic_crypto_client_stream.h
+++ b/net/quic/quic_crypto_client_stream.h
@@ -13,7 +13,6 @@
namespace net {
-class QuicConfig;
class QuicSession;
namespace test {
@@ -23,7 +22,6 @@ class CryptoTestUtils;
class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
public:
QuicCryptoClientStream(const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config);
virtual ~QuicCryptoClientStream();
@@ -61,7 +59,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
// connection has sent.
int num_client_hellos_;
- const QuicConfig& config_;
QuicCryptoClientConfig* const crypto_config_;
// Client's connection nonce (4-byte timestamp + 28 random bytes)
diff --git a/net/quic/quic_crypto_client_stream_factory.h b/net/quic/quic_crypto_client_stream_factory.h
index 4d0eb1e..4fa5f57 100644
--- a/net/quic/quic_crypto_client_stream_factory.h
+++ b/net/quic/quic_crypto_client_stream_factory.h
@@ -22,7 +22,6 @@ class NET_EXPORT QuicCryptoClientStreamFactory {
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config) = 0;
};
diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc
index c913964..90b9122 100644
--- a/net/quic/quic_crypto_client_stream_test.cc
+++ b/net/quic/quic_crypto_client_stream_test.cc
@@ -8,6 +8,7 @@
#include "net/quic/crypto/aes_128_gcm_encrypter.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_protocol.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/simple_quic_framer.h"
@@ -50,10 +51,10 @@ class QuicCryptoClientStreamTest : public ::testing::Test {
QuicCryptoClientStreamTest()
: addr_(),
connection_(new PacketSavingConnection(1, addr_, true)),
- session_(connection_, true),
- stream_(kServerHostname, config_, &session_, &crypto_config_) {
+ session_(connection_, QuicConfig(), true),
+ stream_(kServerHostname, &session_, &crypto_config_) {
session_.SetCryptoStream(&stream_);
- config_.SetDefaults();
+ session_.config()->SetDefaults();
crypto_config_.SetDefaults();
}
@@ -73,7 +74,6 @@ class QuicCryptoClientStreamTest : public ::testing::Test {
QuicCryptoClientStream stream_;
CryptoHandshakeMessage message_;
scoped_ptr<QuicData> message_data_;
- QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
};
@@ -137,10 +137,13 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
CompleteCryptoHandshake();
- const QuicNegotiatedParameters& params(stream_.negotiated_params());
- EXPECT_EQ(kQBIC, params.congestion_control);
- EXPECT_EQ(300, params.idle_connection_state_lifetime.ToSeconds());
- EXPECT_EQ(0, params.keepalive_timeout.ToSeconds());
+ const QuicConfig* config = session_.config();
+ EXPECT_EQ(kQBIC, config->congestion_control());
+ EXPECT_EQ(kDefaultTimeoutSecs,
+ config->idle_connection_state_lifetime().ToSeconds());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ config->max_streams_per_connection());
+ EXPECT_EQ(0, config->keepalive_timeout().ToSeconds());
const QuicCryptoNegotiatedParameters& crypto_params(
stream_.crypto_negotiated_params());
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
index 57c1076..c87c6d5 100644
--- a/net/quic/quic_crypto_server_stream.cc
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -14,11 +14,9 @@
namespace net {
QuicCryptoServerStream::QuicCryptoServerStream(
- const QuicConfig& config,
const QuicCryptoServerConfig& crypto_config,
QuicSession* session)
: QuicCryptoStream(session),
- config_(config),
crypto_config_(crypto_config) {
}
@@ -58,14 +56,15 @@ void QuicCryptoServerStream::OnHandshakeMessage(
}
// If we are returning a SHLO then we accepted the handshake.
- error = config_.ProcessFinalPeerHandshake(
- message, CryptoUtils::LOCAL_PRIORITY, &negotiated_params_,
- &error_details);
+ QuicConfig* config = session()->config();
+ error = config->ProcessClientHello(message, &error_details);
if (error != QUIC_NO_ERROR) {
CloseConnectionWithDetails(error, error_details);
return;
}
+ config->ToHandshakeMessage(&reply);
+
// Receiving a full CHLO implies the client is prepared to decrypt with
// the new server write key. We can start to encrypt with the new server
// write key.
diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h
index 47c1478..1c7ac06 100644
--- a/net/quic/quic_crypto_server_stream.h
+++ b/net/quic/quic_crypto_server_stream.h
@@ -15,7 +15,6 @@ namespace net {
class CryptoHandshakeMessage;
class QuicCryptoServerConfig;
-class QuicNegotiatedParameters;
class QuicSession;
namespace test {
@@ -24,8 +23,7 @@ class CryptoTestUtils;
class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
public:
- QuicCryptoServerStream(const QuicConfig& config,
- const QuicCryptoServerConfig& crypto_config,
+ QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config,
QuicSession* session);
explicit QuicCryptoServerStream(QuicSession* session);
virtual ~QuicCryptoServerStream();
@@ -37,9 +35,6 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
private:
friend class test::CryptoTestUtils;
- // config_ contains non-crypto parameters that are negotiated in the crypto
- // handshake.
- const QuicConfig& config_;
// crypto_config_ contains crypto parameters for the handshake.
const QuicCryptoServerConfig& crypto_config_;
};
diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc
index d12597b..73469db 100644
--- a/net/quic/quic_crypto_server_stream_test.cc
+++ b/net/quic/quic_crypto_server_stream_test.cc
@@ -61,9 +61,10 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ?
ip_ : IPAddressNumber(), 1),
connection_(new PacketSavingConnection(guid_, addr_, true)),
- session_(connection_, true),
+ session_(connection_, QuicConfig(), true),
crypto_config_(QuicCryptoServerConfig::TESTING),
- stream_(config_, crypto_config_, &session_) {
+ stream_(crypto_config_, &session_) {
+ session_.config()->SetDefaults();
session_.SetCryptoStream(&stream_);
// We advance the clock initially because the default time is zero and the
// strike register worries that we've just overflowed a uint32 time.
@@ -72,8 +73,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
// crypto_config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting());
CryptoTestUtils::SetupCryptoServerConfigForTest(
- connection_->clock(), connection_->random_generator(), &config_,
- &crypto_config_);
+ connection_->clock(), connection_->random_generator(),
+ session_.config(), &crypto_config_);
}
void ConstructHandshakeMessage() {
@@ -82,7 +83,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
}
int CompleteCryptoHandshake() {
- return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_);
+ return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_,
+ client_options_);
}
protected:
@@ -96,6 +98,7 @@ class QuicCryptoServerStreamTest : public ::testing::Test {
QuicCryptoServerStream stream_;
CryptoHandshakeMessage message_;
scoped_ptr<QuicData> message_data_;
+ CryptoTestUtils::FakeClientOptions client_options_;
};
TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) {
@@ -144,18 +147,15 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) {
client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
- scoped_ptr<TestSession> client_session(new TestSession(client_conn, true));
- scoped_ptr<TestSession> server_session(new TestSession(server_conn, true));
-
QuicConfig client_config;
+ scoped_ptr<TestSession> client_session(
+ new TestSession(client_conn, client_config, false));
+ client_session->config()->SetDefaults();
QuicCryptoClientConfig client_crypto_config;
-
- client_config.SetDefaults();
client_crypto_config.SetDefaults();
scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream(
- "test.example.com", client_config, client_session.get(),
- &client_crypto_config));
+ "test.example.com", client_session.get(), &client_crypto_config));
client_session->SetCryptoStream(client.get());
// Do a first handshake in order to prime the client config with the server's
@@ -163,9 +163,11 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) {
CHECK(client->CryptoConnect());
CHECK_EQ(1u, client_conn->packets_.size());
+ scoped_ptr<TestSession> server_session(
+ new TestSession(server_conn, config_, true));
+ server_session->config()->SetDefaults();
scoped_ptr<QuicCryptoServerStream> server(
- new QuicCryptoServerStream(config_, crypto_config_,
- server_session.get()));
+ new QuicCryptoServerStream(crypto_config_, server_session.get()));
server_session->SetCryptoStream(server.get());
CryptoTestUtils::CommunicateHandshakeMessages(
@@ -185,14 +187,15 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) {
// This causes the client's nonce to be different and thus stops the
// strike-register from rejecting the repeated nonce.
client_conn->random_generator()->Reseed(NULL, 0);
- client_session.reset(new TestSession(client_conn, true));
- server_session.reset(new TestSession(server_conn, true));
+ client_session.reset(new TestSession(client_conn, client_config, false));
+ client_session->config()->SetDefaults();
+ server_session.reset(new TestSession(server_conn, config_, true));
+ server_session->config()->SetDefaults();
client.reset(new QuicCryptoClientStream(
- "test.example.com", client_config, client_session.get(),
- &client_crypto_config));
+ "test.example.com", client_session.get(), &client_crypto_config));
client_session->SetCryptoStream(client.get());
- server.reset(new QuicCryptoServerStream(config_, crypto_config_,
+ server.reset(new QuicCryptoServerStream(crypto_config_,
server_session.get()));
server_session->SetCryptoStream(server.get());
@@ -230,6 +233,22 @@ TEST_F(QuicCryptoServerStreamTest, BadMessageType) {
stream_.ProcessData(message_data_->data(), message_data_->length());
}
+TEST_F(QuicCryptoServerStreamTest, WithoutCertificates) {
+ if (!Aes128GcmEncrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ crypto_config_.SetProofSource(NULL);
+ client_options_.dont_verify_certs = true;
+
+ // Only 2 client hellos need to be sent in the no-certs case: one to get the
+ // source-address token and the second to finish.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(stream_.encryption_established());
+ EXPECT_TRUE(stream_.handshake_confirmed());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
index 70bf4d3..2f06e3b 100644
--- a/net/quic/quic_crypto_stream.cc
+++ b/net/quic/quic_crypto_stream.cc
@@ -57,11 +57,6 @@ void QuicCryptoStream::SendHandshakeMessage(
WriteData(string(data.data(), data.length()), false);
}
-const QuicNegotiatedParameters&
-QuicCryptoStream::negotiated_params() const {
- return negotiated_params_;
-}
-
const QuicCryptoNegotiatedParameters&
QuicCryptoStream::crypto_negotiated_params() const {
return crypto_negotiated_params_;
diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h
index 1352f2e..bdae59e 100644
--- a/net/quic/quic_crypto_stream.h
+++ b/net/quic/quic_crypto_stream.h
@@ -46,7 +46,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
bool encryption_established() { return encryption_established_; }
bool handshake_confirmed() { return handshake_confirmed_; }
- const QuicNegotiatedParameters& negotiated_params() const;
const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const;
protected:
@@ -57,7 +56,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
bool encryption_established_;
bool handshake_confirmed_;
- QuicNegotiatedParameters negotiated_params_;
QuicCryptoNegotiatedParameters crypto_negotiated_params_;
private:
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 67a376e..0e56382 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -102,6 +102,14 @@ size_t QuicFramer::GetMinGoAwayFrameSize() {
kQuicStreamIdSize;
}
+// static
+// TODO(satyamshekhar): 16 - Crypto hash for integrity. Not a static value. Use
+// QuicEncrypter::GetMaxPlaintextSize.
+size_t QuicFramer::GetMaxUnackedPackets(bool include_version) {
+ return (kMaxPacketSize - GetPacketHeaderSize(include_version) -
+ GetMinAckFrameSize() - 16) / kSequenceNumberSize;
+}
+
bool QuicFramer::IsSupportedVersion(QuicTag version) {
return version == kQuicVersion1;
}
@@ -313,6 +321,8 @@ QuicEncryptedPacket* QuicFramer::ConstructVersionNegotiationPacket(
}
bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ // TODO(satyamshekhar): Don't RaiseError (and close the connection) for
+ // invalid (unauthenticated) packets.
DCHECK(!reader_.get());
reader_.reset(new QuicDataReader(packet.data(), packet.length()));
@@ -409,15 +419,14 @@ bool QuicFramer::ProcessPublicResetPacket(
const QuicPacketPublicHeader& public_header) {
QuicPublicResetPacket packet(public_header);
if (!reader_->ReadUInt64(&packet.nonce_proof)) {
- // TODO(satyamshekhar): Raise error.
set_detailed_error("Unable to read nonce proof.");
- return false;
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
// TODO(satyamshekhar): validate nonce to protect against DoS.
if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) {
set_detailed_error("Unable to read rejected sequence number.");
- return false;
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
}
visitor_->OnPublicResetPacket(packet);
return true;
@@ -431,8 +440,9 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header,
header->entropy_hash = GetPacketEntropyHash(*header);
- // TODO(satyamshekhar): Don't process if the visitor refuses the header.
- visitor_->OnPacketHeader(*header);
+ if (!visitor_->OnPacketHeader(*header)) {
+ return true;
+ }
if (payload.length() > kMaxPacketSize) {
set_detailed_error("Revived packet too large.");
@@ -704,6 +714,13 @@ bool QuicFramer::ProcessFrameData() {
if (!ProcessConnectionCloseFrame(&frame)) {
return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
}
+
+ if (!visitor_->OnAckFrame(frame.ack_frame)) {
+ DLOG(INFO) << "Visitor asked to stopped further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+
if (!visitor_->OnConnectionCloseFrame(frame)) {
DLOG(INFO) << "Visitor asked to stopped further processing.";
// Returning true since there was no parsing error.
@@ -988,12 +1005,6 @@ bool QuicFramer::ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame) {
return false;
}
- if (!visitor_->OnAckFrame(frame->ack_frame)) {
- DLOG(INFO) << "Visitor asked to stopped further processing.";
- // Returning true since there was no parsing error.
- return true;
- }
-
return true;
}
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
index 8286844..b13a257 100644
--- a/net/quic/quic_framer.h
+++ b/net/quic/quic_framer.h
@@ -231,6 +231,9 @@ class NET_EXPORT_PRIVATE QuicFramer {
static size_t GetMinConnectionCloseFrameSize();
// Size in bytes of all GoAway frame fields without the reason phrase.
static size_t GetMinGoAwayFrameSize();
+ // The maximum number of nacks which can be transmitted in a single ack packet
+ // without exceeding kMaxPacketSize.
+ static size_t GetMaxUnackedPackets(bool include_version);
// Size in bytes required for a serialized version negotiation packet
size_t GetVersionNegotiationPacketSize(size_t number_versions);
diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc
index 6986443..701e2bb 100644
--- a/net/quic/quic_framer_test.cc
+++ b/net/quic/quic_framer_test.cc
@@ -572,7 +572,7 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) {
// public flags (version)
0x01,
// version tag
- 'Q', '0', '0', '1',
+ 'Q', '0', '0', '2',
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -817,7 +817,7 @@ TEST_F(QuicFramerTest, StreamFrameWithVersion) {
// public flags (version)
0x01,
// version tag
- 'Q', '0', '0', '1',
+ 'Q', '0', '0', '2',
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -1608,14 +1608,21 @@ TEST_F(QuicFramerTest, PublicResetPacket) {
DLOG(INFO) << "iteration: " << i;
if (i < kPublicFlagsOffset) {
expected_error = "Unable to read GUID.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
} else if (i < kPublicResetPacketNonceProofOffset) {
expected_error = "Unable to read public flags.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
} else if (i < kPublicResetPacketRejectedSequenceNumberOffset) {
expected_error = "Unable to read nonce proof.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
} else {
expected_error = "Unable to read rejected sequence number.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
}
- CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
}
}
@@ -1626,7 +1633,7 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) {
// public flags (version)
0x01,
// version tag
- 'Q', '0', '0', '1',
+ 'Q', '0', '0', '2',
'Q', '2', '.', '0',
};
@@ -1825,7 +1832,7 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) {
// public flags (version)
0x01,
// version tag
- 'Q', '0', '0', '1',
+ 'Q', '0', '0', '2',
// packet sequence number
0xBC, 0x9A, 0x78, 0x56,
0x34, 0x12,
@@ -1873,7 +1880,7 @@ TEST_F(QuicFramerTest, ConstructVersionNegotiationPacket) {
// public flags (version)
0x01,
// version tag
- 'Q', '0', '0', '1',
+ 'Q', '0', '0', '2',
'Q', '2', '.', '0',
};
@@ -2801,5 +2808,65 @@ TEST_F(QuicFramerTest, StopPacketProcessing) {
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
}
+TEST_F(QuicFramerTest, ConnectionCloseWithInvalidAck) {
+ unsigned char packet[] = {
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // public flags
+ 0x00,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (connection close frame)
+ 0x05,
+ // error code
+ 0x11, 0x00, 0x00, 0x00,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+
+ // Ack frame.
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xE0,
+ // least packet sequence number awaiting an ack
+ 0xA0, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Infinite delta time.
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ // num missing packets
+ 0x01,
+ // missing packet
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ MockFramerVisitor visitor;
+ framer_.set_visitor(&visitor);
+ EXPECT_CALL(visitor, OnPacket());
+ EXPECT_CALL(visitor, OnPacketHeader(_));
+ EXPECT_CALL(visitor, OnAckFrame(_)).WillOnce(Return(false));
+ EXPECT_CALL(visitor, OnConnectionCloseFrame(_)).Times(0);
+ EXPECT_CALL(visitor, OnPacketComplete());
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index d7fafe0..ff82ab4 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -179,8 +179,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
crypto_config_.SetDefaults();
session_.reset(new QuicClientSession(connection_, socket, NULL,
&crypto_client_stream_factory_,
- "www.google.com", &crypto_config_,
- NULL));
+ "www.google.com", QuicConfig(),
+ &crypto_config_, NULL));
session_->GetCryptoStream()->CryptoConnect();
EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed());
QuicReliableClientStream* stream =
@@ -265,7 +265,6 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
testing::StrictMock<MockConnectionVisitor> visitor_;
scoped_ptr<QuicHttpStream> stream_;
scoped_ptr<QuicClientSession> session_;
- QuicConfig* config_;
QuicCryptoClientConfig crypto_config_;
TestCompletionCallback callback_;
HttpRequestInfo request_;
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 31fab86..f4c5e95 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -93,7 +93,10 @@ const QuicStreamId kCryptoStreamId = 1;
// Value which indicates this packet is not FEC protected.
const uint8 kNoFecOffset = 0xFF;
-const int64 kDefaultTimeoutUs = 600000000; // 10 minutes.
+// This is the default network timeout a for connection till the crypto
+// handshake succeeds and the negotiated timeout from the handshake is received.
+const int64 kDefaultInitialTimeoutSecs = 30; // 30 secs.
+const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes.
enum Retransmission {
NOT_RETRANSMISSION,
@@ -173,6 +176,8 @@ enum QuicErrorCode {
QUIC_INVALID_ACK_DATA,
// Version negotiation packet is malformed.
QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
+ // Public RST packet is malformed.
+ QUIC_INVALID_PUBLIC_RST_PACKET,
// There was an error decrypting.
QUIC_DECRYPTION_FAILURE,
// There was an error encrypting.
@@ -195,9 +200,17 @@ enum QuicErrorCode {
QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED,
// The Header ID for a stream was too far from the previous.
QUIC_INVALID_HEADER_ID,
-
+ // Negotiable parameter received during handshake had invalid value.
+ QUIC_INVALID_NEGOTIATED_VALUE,
+ // There was an error decompressing data.
+ QUIC_DECOMPRESSION_FAILURE,
// We hit our prenegotiated (or default) timeout
QUIC_CONNECTION_TIMED_OUT,
+ // There was an error encountered migrating addresses
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ // There was an error while writing the packet.
+ QUIC_PACKET_WRITE_ERROR,
+
// Crypto errors.
@@ -258,7 +271,7 @@ const QuicTag kUnsupportedVersion = -1;
// Each time the wire format changes, this need needs to be incremented.
// At some point, we will actually freeze the wire format and make an official
// version number, but this works for now.
-const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '1');
+const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '2');
#undef TAG
// MakeQuicTag returns a value given the four bytes. For example:
diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc
index 2517bac..12402fd 100644
--- a/net/quic/quic_reliable_client_stream_test.cc
+++ b/net/quic/quic_reliable_client_stream_test.cc
@@ -42,7 +42,6 @@ class QuicReliableClientStreamTest : public ::testing::Test {
testing::StrictMock<MockDelegate> delegate_;
MockSession session_;
QuicReliableClientStream stream_;
- QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
};
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 66879300..5d5df18 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -64,9 +64,12 @@ class VisitorShim : public QuicConnectionVisitorInterface {
QuicSession* session_;
};
-QuicSession::QuicSession(QuicConnection* connection, bool is_server)
+QuicSession::QuicSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server)
: connection_(connection),
visitor_shim_(new VisitorShim(this)),
+ config_(config),
max_open_streams_(kDefaultMaxStreamsPerConnection),
next_stream_id_(is_server ? 2 : 3),
is_server_(is_server),
@@ -214,11 +217,17 @@ bool QuicSession::IsCryptoHandshakeConfirmed() {
void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
if (event == QuicSession::HANDSHAKE_CONFIRMED) {
- connection_->SetConnectionTimeout(
- GetCryptoStream()->negotiated_params().idle_connection_state_lifetime);
+ LOG_IF(DFATAL, !config_.negotiated())
+ << "Handshake confirmed without parameter negotiation.";
+ connection_->SetConnectionTimeout(config_.idle_connection_state_lifetime());
+ max_open_streams_ = config_.max_streams_per_connection();
}
}
+QuicConfig* QuicSession::config() {
+ return &config_;
+}
+
void QuicSession::ActivateStream(ReliableQuicStream* stream) {
DLOG(INFO) << "num_streams: " << stream_map_.size()
<< ". activating " << stream->id();
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index eff0546..00003d4 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -50,7 +50,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
HANDSHAKE_CONFIRMED,
};
- QuicSession(QuicConnection* connection, bool is_server);
+ QuicSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server);
virtual ~QuicSession();
@@ -102,6 +104,8 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Servers will simply call it once with HANDSHAKE_CONFIRMED.
virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event);
+ virtual QuicConfig* config();
+
// Returns true if the stream existed previously and has been closed.
// Returns false if the stream is still active or if the stream has
// not yet been created.
@@ -176,6 +180,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
return max_open_streams_;
}
+ void set_max_open_streams(size_t max_open_streams) {
+ max_open_streams_ = max_open_streams;
+ }
+
private:
friend class test::QuicSessionPeer;
friend class VisitorShim;
@@ -195,8 +203,10 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
QuicSpdyDecompressor decompressor_;
QuicSpdyCompressor compressor_;
+ QuicConfig config_;
+
// Returns the maximum number of streams this connection can open.
- const size_t max_open_streams_;
+ size_t max_open_streams_;
// Map from StreamId to pointers to streams that are owned by the caller.
ReliableStreamMap stream_map_;
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 02b9853..338caed 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -3,12 +3,14 @@
// found in the LICENSE file.
#include "net/quic/quic_session.h"
-#include "net/quic/quic_connection.h"
#include <set>
#include "base/hash_tables.h"
#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,6 +34,12 @@ class TestCryptoStream : public QuicCryptoStream {
const CryptoHandshakeMessage& message) OVERRIDE {
encryption_established_ = true;
handshake_confirmed_ = true;
+ CryptoHandshakeMessage msg;
+ string error_details;
+ session()->config()->ToHandshakeMessage(&msg);
+ const QuicErrorCode error = session()->config()->ProcessClientHello(
+ msg, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
}
};
@@ -52,8 +60,9 @@ class TestStream : public ReliableQuicStream {
class TestSession : public QuicSession {
public:
TestSession(QuicConnection* connection, bool is_server)
- : QuicSession(connection, is_server),
+ : QuicSession(connection, QuicConfig(), is_server),
crypto_stream_(this) {
+ config()->SetDefaults();
}
virtual QuicCryptoStream* GetCryptoStream() OVERRIDE {
@@ -84,6 +93,7 @@ class TestSession : public QuicSession {
}
TestCryptoStream crypto_stream_;
+ QuicConfig config_;
};
class QuicSessionTest : public ::testing::Test {
@@ -166,6 +176,16 @@ TEST_F(QuicSessionTest, StreamIdTooLarge) {
session_.GetIncomingReliableStream(105);
}
+TEST_F(QuicSessionTest, DecompressionError) {
+ ReliableQuicStream* stream = session_.GetIncomingReliableStream(3);
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE));
+ const char data[] =
+ "\1\0\0\0" // headers id
+ "\0\0\0\4" // length
+ "abcd"; // invalid compressed data
+ stream->ProcessRawData(data, arraysize(data));
+}
+
TEST_F(QuicSessionTest, OnCanWrite) {
TestStream* stream2 = session_.CreateOutgoingReliableStream();
TestStream* stream4 = session_.CreateOutgoingReliableStream();
@@ -213,6 +233,15 @@ TEST_F(QuicSessionTest, SendGoAway) {
EXPECT_FALSE(session_.GetIncomingReliableStream(3u));
}
+TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
+ EXPECT_EQ(kDefaultInitialTimeoutSecs,
+ QuicConnectionPeer::GetTimeout(connection_).ToSeconds());
+ CryptoHandshakeMessage msg;
+ session_.crypto_stream_.OnHandshakeMessage(msg);
+ EXPECT_EQ(kDefaultTimeoutSecs,
+ QuicConnectionPeer::GetTimeout(connection_).ToSeconds());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_spdy_compressor.cc b/net/quic/quic_spdy_compressor.cc
index 4497c59..4f67e26 100644
--- a/net/quic/quic_spdy_compressor.cc
+++ b/net/quic/quic_spdy_compressor.cc
@@ -12,7 +12,7 @@ using std::string;
namespace net {
QuicSpdyCompressor::QuicSpdyCompressor()
- : spdy_framer_(3),
+ : spdy_framer_(kSpdyVersion3),
header_sequence_id_(1) {
spdy_framer_.set_enable_compression(true);
}
diff --git a/net/quic/quic_spdy_decompressor.cc b/net/quic/quic_spdy_decompressor.cc
index f96e846..0d58937 100644
--- a/net/quic/quic_spdy_decompressor.cc
+++ b/net/quic/quic_spdy_decompressor.cc
@@ -75,7 +75,7 @@ bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id,
}
QuicSpdyDecompressor::QuicSpdyDecompressor()
- : spdy_framer_(3),
+ : spdy_framer_(kSpdyVersion3),
spdy_visitor_(new SpdyFramerVisitor(NULL)),
current_header_id_(1),
has_current_compressed_size_(false),
@@ -116,9 +116,11 @@ size_t QuicSpdyDecompressor::DecompressData(StringPiece data,
min(current_compressed_size_ - compressed_bytes_consumed_,
static_cast<uint32>(data.length()));
if (bytes_to_consume > 0) {
- bool success = spdy_framer_.IncrementallyDecompressControlFrameHeaderData(
- current_header_id_, data.data(), bytes_to_consume);
- DCHECK(success);
+ if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData(
+ current_header_id_, data.data(), bytes_to_consume)) {
+ visitor->OnDecompressionError();
+ return bytes_consumed;
+ }
compressed_bytes_consumed_ += bytes_to_consume;
bytes_consumed += bytes_to_consume;
}
diff --git a/net/quic/quic_spdy_decompressor.h b/net/quic/quic_spdy_decompressor.h
index 3fd50783..a56c479 100644
--- a/net/quic/quic_spdy_decompressor.h
+++ b/net/quic/quic_spdy_decompressor.h
@@ -27,6 +27,7 @@ class NET_EXPORT_PRIVATE QuicSpdyDecompressor {
public:
virtual ~Visitor() {}
virtual bool OnDecompressedData(base::StringPiece data) = 0;
+ virtual void OnDecompressionError() = 0;
};
QuicSpdyDecompressor();
diff --git a/net/quic/quic_spdy_decompressor_test.cc b/net/quic/quic_spdy_decompressor_test.cc
index b2fbb37..1e63396 100644
--- a/net/quic/quic_spdy_decompressor_test.cc
+++ b/net/quic/quic_spdy_decompressor_test.cc
@@ -52,6 +52,22 @@ TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) {
EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data());
}
+TEST_F(QuicSpdyDecompressorTest, DecompressError) {
+ SpdyHeaderBlock headers;
+ headers[":host"] = "www.google.com";
+ headers[":path"] = "/index.hml";
+ headers[":scheme"] = "https";
+
+ EXPECT_EQ(1u, decompressor_.current_header_id());
+ string compressed_headers = compressor_.CompressHeaders(headers).substr(4);
+ compressed_headers[compressed_headers.length() - 1] ^= 0x01;
+ EXPECT_NE(compressed_headers.length(),
+ decompressor_.DecompressData(compressed_headers, &visitor_));
+
+ EXPECT_TRUE(visitor_.error());
+ EXPECT_EQ("", visitor_.data());
+}
+
} // namespace
} // namespace test
} // namespace net
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 5ac6bf58..007fc88 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -372,7 +372,6 @@ QuicClientSession* QuicStreamFactory::CreateSession(
DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt),
net_log.net_log(), net_log.source());
socket->Connect(addr);
- socket->GetLocalAddress(&addr);
QuicConnectionHelper* helper = new QuicConnectionHelper(
MessageLoop::current()->message_loop_proxy(),
@@ -387,7 +386,7 @@ QuicClientSession* QuicStreamFactory::CreateSession(
QuicClientSession* session =
new QuicClientSession(connection, socket, this,
quic_crypto_client_stream_factory_,
- host_port_proxy_pair.first.host(),
+ host_port_proxy_pair.first.host(), QuicConfig(),
crypto_config, net_log.net_log());
all_sessions_.insert(session); // owning pointer
return session;
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index 228b796..a42993c 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -4,8 +4,13 @@
#include "net/quic/quic_utils.h"
+#include <ctype.h>
+
#include "base/logging.h"
#include "base/port.h"
+#include "base/strings/string_number_conversions.h"
+
+using std::string;
namespace net {
@@ -49,6 +54,53 @@ uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) {
}
// static
+bool QuicUtils::FindMutualTag(const QuicTagVector& our_tags_vector,
+ const QuicTag* their_tags,
+ size_t num_their_tags,
+ Priority priority,
+ QuicTag* out_result,
+ size_t* out_index) {
+ if (our_tags_vector.empty()) {
+ return false;
+ }
+ const size_t num_our_tags = our_tags_vector.size();
+ const QuicTag* our_tags = &our_tags_vector[0];
+
+ size_t num_priority_tags, num_inferior_tags;
+ const QuicTag* priority_tags;
+ const QuicTag* inferior_tags;
+ if (priority == LOCAL_PRIORITY) {
+ num_priority_tags = num_our_tags;
+ priority_tags = our_tags;
+ num_inferior_tags = num_their_tags;
+ inferior_tags = their_tags;
+ } else {
+ num_priority_tags = num_their_tags;
+ priority_tags = their_tags;
+ num_inferior_tags = num_our_tags;
+ inferior_tags = our_tags;
+ }
+
+ for (size_t i = 0; i < num_priority_tags; i++) {
+ for (size_t j = 0; j < num_inferior_tags; j++) {
+ if (priority_tags[i] == inferior_tags[j]) {
+ *out_result = priority_tags[i];
+ if (out_index) {
+ if (priority == LOCAL_PRIORITY) {
+ *out_index = j;
+ } else {
+ *out_index = i;
+ }
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
void QuicUtils::SerializeUint128(uint128 v, uint8* out) {
const uint64 lo = Uint128Low64(v);
const uint64 hi = Uint128High64(v);
@@ -100,6 +152,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PUBLIC_RST_PACKET);
RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE);
RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE);
RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE);
@@ -124,7 +177,11 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED);
RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE);
+ RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE);
RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT);
+ RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS);
+ RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR);
RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
@@ -139,4 +196,29 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
return "INVALID_ERROR_CODE";
}
+// static
+string QuicUtils::TagToString(QuicTag tag) {
+ char chars[4];
+ bool ascii = true;
+ const QuicTag orig_tag = tag;
+
+ for (size_t i = 0; i < sizeof(chars); i++) {
+ chars[i] = tag;
+ if (chars[i] == 0 && i == 3) {
+ chars[i] = ' ';
+ }
+ if (!isprint(static_cast<unsigned char>(chars[i]))) {
+ ascii = false;
+ break;
+ }
+ tag >>= 8;
+ }
+
+ if (ascii) {
+ return string(chars, sizeof(chars));
+ }
+
+ return base::UintToString(orig_tag);
+}
+
} // namespace net
diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h
index 14345eb..e866a07 100644
--- a/net/quic/quic_utils.h
+++ b/net/quic/quic_utils.h
@@ -15,6 +15,11 @@ namespace net {
class NET_EXPORT_PRIVATE QuicUtils {
public:
+ enum Priority {
+ LOCAL_PRIORITY,
+ PEER_PRIORITY,
+ };
+
// returns the 64 bit FNV1a hash of the data. See
// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
static uint64 FNV1a_64_Hash(const char* data, int len);
@@ -23,6 +28,21 @@ class NET_EXPORT_PRIVATE QuicUtils {
// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
static uint128 FNV1a_128_Hash(const char* data, int len);
+ // FindMutualTag sets |out_result| to the first tag in the priority list that
+ // is also in the other list and returns true. If there is no intersection it
+ // returns false.
+ //
+ // Which list has priority is determined by |priority|.
+ //
+ // If |out_index| is non-NULL and a match is found then the index of that
+ // match in |their_tags| is written to |out_index|.
+ static bool FindMutualTag(const QuicTagVector& our_tags,
+ const QuicTag* their_tags,
+ size_t num_their_tags,
+ Priority priority,
+ QuicTag* out_result,
+ size_t* out_index);
+
// SerializeUint128 writes |v| in little-endian form to |out|.
static void SerializeUint128(uint128 v, uint8* out);
@@ -34,6 +54,12 @@ class NET_EXPORT_PRIVATE QuicUtils {
// Returns the name of the QuicErrorCode as a char*
static const char* ErrorToString(QuicErrorCode error);
+
+ // TagToString is a utility function for pretty-printing handshake messages
+ // that converts a tag to a string. It will try to maintain the human friendly
+ // name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number
+ // if not.
+ static std::string TagToString(QuicTag tag);
};
} // namespace net
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index cadbe9a..c293eed 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -365,6 +365,11 @@ bool ReliableQuicStream::OnDecompressedData(StringPiece data) {
return true;
}
+void ReliableQuicStream::OnDecompressionError() {
+ session_->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE);
+}
+
+
void ReliableQuicStream::CloseWriteSide() {
if (write_side_closed_) {
return;
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 726cc9c..a22f90d 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -76,6 +76,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
virtual uint32 ProcessData(const char* data, uint32 data_len) = 0;
virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
+ virtual void OnDecompressionError() OVERRIDE;
// Called to close the stream from this end.
virtual void Close(QuicRstStreamErrorCode error);
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index 058157e..4e285ad 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -102,6 +102,10 @@ void MovePackets(PacketSavingConnection* source_conn,
} // anonymous namespace
+CryptoTestUtils::FakeClientOptions::FakeClientOptions()
+ : dont_verify_certs(false) {
+}
+
// static
void CryptoTestUtils::CommunicateHandshakeMessages(
PacketSavingConnection* a_conn,
@@ -135,16 +139,15 @@ int CryptoTestUtils::HandshakeWithFakeServer(
IPEndPoint addr = IPEndPoint(ip, 1);
PacketSavingConnection* server_conn =
new PacketSavingConnection(guid, addr, true);
- TestSession server_session(server_conn, true);
+ TestSession server_session(server_conn, QuicConfig(), true);
- QuicConfig config;
QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING);
SetupCryptoServerConfigForTest(
server_session.connection()->clock(),
server_session.connection()->random_generator(),
- &config, &crypto_config);
+ server_session.config(), &crypto_config);
- QuicCryptoServerStream server(config, crypto_config, &server_session);
+ QuicCryptoServerStream server(crypto_config, &server_session);
server_session.SetCryptoStream(&server);
// The client's handshake must have been started already.
@@ -160,22 +163,24 @@ int CryptoTestUtils::HandshakeWithFakeServer(
// static
int CryptoTestUtils::HandshakeWithFakeClient(
PacketSavingConnection* server_conn,
- QuicCryptoServerStream* server) {
+ QuicCryptoServerStream* server,
+ const FakeClientOptions& options) {
QuicGuid guid(1);
IPAddressNumber ip;
CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
IPEndPoint addr = IPEndPoint(ip, 1);
PacketSavingConnection* client_conn =
new PacketSavingConnection(guid, addr, false);
- TestSession client_session(client_conn, true);
- QuicConfig config;
+ TestSession client_session(client_conn, QuicConfig(), false);
QuicCryptoClientConfig crypto_config;
- config.SetDefaults();
+ client_session.config()->SetDefaults();
crypto_config.SetDefaults();
// TODO(rtenneti): Enable testing of ProofVerifier.
- // crypto_config.SetProofVerifier(ProofVerifierForTesting());
- QuicCryptoClientStream client("test.example.com", config, &client_session,
+ // if (!options.dont_verify_certs) {
+ // crypto_config.SetProofVerifier(ProofVerifierForTesting());
+ // }
+ QuicCryptoClientStream client("test.example.com", &client_session,
&crypto_config);
client_session.SetCryptoStream(&client);
@@ -196,15 +201,9 @@ void CryptoTestUtils::SetupCryptoServerConfigForTest(
QuicConfig* config,
QuicCryptoServerConfig* crypto_config) {
config->SetDefaults();
- CryptoHandshakeMessage extra_tags;
- config->ToHandshakeMessage(&extra_tags);
-
scoped_ptr<CryptoHandshakeMessage> scfg(
crypto_config->AddDefaultConfig(
- rand, clock, extra_tags, QuicCryptoServerConfig::kDefaultExpiry));
- if (!config->SetFromHandshakeMessage(*scfg)) {
- CHECK(false) << "Crypto config could not be parsed by QuicConfig.";
- }
+ rand, clock, QuicCryptoServerConfig::kDefaultExpiry));
}
// static
@@ -274,8 +273,8 @@ class MockCommonCertSets : public CommonCertSets {
};
CommonCertSets* CryptoTestUtils::MockCommonCertSets(StringPiece cert,
- uint64 hash,
- uint32 index) {
+ uint64 hash,
+ uint32 index) {
return new class MockCommonCertSets(cert, hash, index);
}
diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h
index 62748a3..3a4fdd4 100644
--- a/net/quic/test_tools/crypto_test_utils.h
+++ b/net/quic/test_tools/crypto_test_utils.h
@@ -32,13 +32,24 @@ class PacketSavingConnection;
class CryptoTestUtils {
public:
+ // FakeClientOptions bundles together a number of options for configuring
+ // HandshakeWithFakeClient.
+ struct FakeClientOptions {
+ FakeClientOptions();
+
+ // If dont_verify_certs is true then no ProofVerifier is set on the client.
+ // Thus no certificates will be requested or checked.
+ bool dont_verify_certs;
+ };
+
// returns: the number of client hellos that the client sent.
static int HandshakeWithFakeServer(PacketSavingConnection* client_conn,
QuicCryptoClientStream* client);
// returns: the number of client hellos that the client sent.
static int HandshakeWithFakeClient(PacketSavingConnection* server_conn,
- QuicCryptoServerStream* server);
+ QuicCryptoServerStream* server,
+ const FakeClientOptions& options);
// SetupCryptoServerConfigForTest configures |config| and |crypto_config|
// with sensible defaults for testing.
diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc
index afaa570..dda5965 100644
--- a/net/quic/test_tools/mock_crypto_client_stream.cc
+++ b/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -8,11 +8,10 @@ namespace net {
MockCryptoClientStream::MockCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config,
HandshakeMode handshake_mode)
- : QuicCryptoClientStream(server_hostname, config, session, crypto_config),
+ : QuicCryptoClientStream(server_hostname, session, crypto_config),
handshake_mode_(handshake_mode) {
}
diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h
index 4546d23..3351dec 100644
--- a/net/quic/test_tools/mock_crypto_client_stream.h
+++ b/net/quic/test_tools/mock_crypto_client_stream.h
@@ -34,7 +34,6 @@ class MockCryptoClientStream : public QuicCryptoClientStream {
MockCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config,
HandshakeMode handshake_mode);
diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/net/quic/test_tools/mock_crypto_client_stream_factory.cc
index 20926ce..7578790 100644
--- a/net/quic/test_tools/mock_crypto_client_stream_factory.cc
+++ b/net/quic/test_tools/mock_crypto_client_stream_factory.cc
@@ -18,11 +18,10 @@ MockCryptoClientStreamFactory::MockCryptoClientStreamFactory()
QuicCryptoClientStream*
MockCryptoClientStreamFactory::CreateQuicCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config) {
- return new MockCryptoClientStream(server_hostname, config, session,
- crypto_config, handshake_mode_);
+ return new MockCryptoClientStream(server_hostname, session, crypto_config,
+ handshake_mode_);
}
} // namespace net
diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.h b/net/quic/test_tools/mock_crypto_client_stream_factory.h
index 01d0c75..e3f2a4a 100644
--- a/net/quic/test_tools/mock_crypto_client_stream_factory.h
+++ b/net/quic/test_tools/mock_crypto_client_stream_factory.h
@@ -22,7 +22,6 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
const string& server_hostname,
- const QuicConfig& config,
QuicSession* session,
QuicCryptoClientConfig* crypto_config) OVERRIDE;
diff --git a/net/quic/test_tools/quic_client_session_peer.cc b/net/quic/test_tools/quic_client_session_peer.cc
new file mode 100644
index 0000000..e88da49
--- /dev/null
+++ b/net/quic/test_tools/quic_client_session_peer.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 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/test_tools/quic_client_session_peer.h"
+
+#include "net/quic/quic_client_session.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicClientSessionPeer::SetMaxOpenStreams(QuicClientSession* session,
+ size_t max_streams,
+ size_t default_streams) {
+ session->config()->set_max_streams_per_connection(max_streams,
+ default_streams);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_client_session_peer.h b/net/quic/test_tools/quic_client_session_peer.h
new file mode 100644
index 0000000..7217d9d
--- /dev/null
+++ b/net/quic/test_tools/quic_client_session_peer.h
@@ -0,0 +1,29 @@
+// Copyright (c) 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_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicClientSession;
+
+namespace test {
+
+class QuicClientSessionPeer {
+ public:
+ static void SetMaxOpenStreams(QuicClientSession* session,
+ size_t max_streams,
+ size_t default_streams);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSessionPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc
index 6816e54..4f2b500 100644
--- a/net/quic/test_tools/quic_connection_peer.cc
+++ b/net/quic/test_tools/quic_connection_peer.cc
@@ -63,6 +63,11 @@ size_t QuicConnectionPeer::GetNumRetransmissionTimeouts(
}
// static
+QuicTime::Delta QuicConnectionPeer::GetTimeout(QuicConnection* connection) {
+ return connection->timeout_;
+}
+
+// static
bool QuicConnectionPeer::IsSavedForRetransmission(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number) {
@@ -121,5 +126,24 @@ void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
framer->SwapCryptersForTest(&connection->framer_);
}
+// static
+void QuicConnectionPeer:: SetMaxPacketsPerRetransmissionAlarm(
+ QuicConnection* connection,
+ int max_packets) {
+ connection->max_packets_per_retransmission_alarm_ = max_packets;
+}
+
+// static
+QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper(
+ QuicConnection* connection) {
+ return connection->helper_.get();
+}
+
+QuicFecGroup* QuicConnectionPeer::GetFecGroup(QuicConnection* connection,
+ int fec_group) {
+ connection->last_header_.fec_group = fec_group;
+ return connection->GetFecGroup();
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h
index 89c6482..e2b4ff9 100644
--- a/net/quic/test_tools/quic_connection_peer.h
+++ b/net/quic/test_tools/quic_connection_peer.h
@@ -12,8 +12,11 @@
namespace net {
struct QuicAckFrame;
+struct QuicPacketHeader;
class QuicConnection;
+class QuicConnectionHelperInterface;
class QuicConnectionVisitorInterface;
+class QuicFecGroup;
class QuicFramer;
class QuicPacketCreator;
class ReceiveAlgorithmInterface;
@@ -43,6 +46,8 @@ class QuicConnectionPeer {
static size_t GetNumRetransmissionTimeouts(QuicConnection* connection);
+ static QuicTime::Delta GetTimeout(QuicConnection* connection);
+
static bool IsSavedForRetransmission(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number);
@@ -70,6 +75,14 @@ class QuicConnectionPeer {
static void SwapCrypters(QuicConnection* connection, QuicFramer* framer);
+ static void SetMaxPacketsPerRetransmissionAlarm(QuicConnection* connection,
+ int max_packets);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicConnection* connection);
+
+ // Set last_header_->fec_group = fec_group and return connection->GetFecGroup
+ static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group);
+
private:
DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer);
};
diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc
index 16b5c41..0a81ca9 100644
--- a/net/quic/test_tools/quic_session_peer.cc
+++ b/net/quic/test_tools/quic_session_peer.cc
@@ -10,9 +10,15 @@ namespace net {
namespace test {
// static
-void QuicSessionPeer::SetNextStreamId(QuicStreamId id, QuicSession* session) {
+void QuicSessionPeer::SetNextStreamId(QuicSession* session, QuicStreamId id) {
session->next_stream_id_ = id;
}
+// static
+void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session,
+ uint32 max_streams) {
+ session->max_open_streams_ = max_streams;
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h
index 16b0c21..9aff659 100644
--- a/net/quic/test_tools/quic_session_peer.h
+++ b/net/quic/test_tools/quic_session_peer.h
@@ -15,7 +15,8 @@ namespace test {
class QuicSessionPeer {
public:
- static void SetNextStreamId(QuicStreamId id, QuicSession* session);
+ static void SetNextStreamId(QuicSession* session, QuicStreamId id);
+ static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams);
private:
DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer);
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index c987277..8821387 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -235,7 +235,7 @@ bool PacketSavingConnection::SendOrQueuePacket(
}
MockSession::MockSession(QuicConnection* connection, bool is_server)
- : QuicSession(connection, is_server) {
+ : QuicSession(connection, QuicConfig(), is_server) {
ON_CALL(*this, WriteData(_, _, _, _))
.WillByDefault(testing::Return(QuicConsumedData(0, false)));
}
@@ -243,8 +243,10 @@ MockSession::MockSession(QuicConnection* connection, bool is_server)
MockSession::~MockSession() {
}
-TestSession::TestSession(QuicConnection* connection, bool is_server)
- : QuicSession(connection, is_server),
+TestSession::TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server)
+ : QuicSession(connection, config, is_server),
crypto_stream_(NULL) {
}
@@ -404,5 +406,9 @@ bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
return true;
}
+void TestDecompressorVisitor::OnDecompressionError() {
+ error_ = true;
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 5ea2a78..8fbf054 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -295,7 +295,9 @@ class MockSession : public QuicSession {
class TestSession : public QuicSession {
public:
- TestSession(QuicConnection* connection, bool is_server);
+ TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server);
virtual ~TestSession();
MOCK_METHOD1(CreateIncomingReliableStream,
@@ -351,11 +353,14 @@ class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
public:
virtual ~TestDecompressorVisitor() {}
virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
+ virtual void OnDecompressionError() OVERRIDE;
string data() { return data_; }
+ bool error() { return error_; }
private:
string data_;
+ bool error_;
};
} // namespace test
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 06cb5f9..aa92856 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -1498,6 +1498,7 @@ const BoundNetLog& MockUDPClientSocket::NetLog() const {
int MockUDPClientSocket::Connect(const IPEndPoint& address) {
connected_ = true;
+ peer_addr_ = address;
return OK;
}
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index c03f9d8..01e9181 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -17,16 +17,22 @@
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_session_peer.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/quic_server.h"
+#include "net/tools/quic/quic_socket_utils.h"
#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "net/tools/quic/test_tools/quic_client_peer.h"
+#include "net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h"
#include "net/tools/quic/test_tools/quic_test_client.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece;
using base::WaitableEvent;
+using net::test::QuicConnectionPeer;
using net::test::QuicSessionPeer;
using net::test::ReliableQuicStreamPeer;
using std::string;
@@ -209,7 +215,7 @@ TEST_F(EndToEndTest, SimpleRequestResponse) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, SimpleRequestResponsev6) {
@@ -220,12 +226,12 @@ TEST_F(EndToEndTest, SimpleRequestResponsev6) {
}
IPAddressNumber ip;
- CHECK(net::ParseIPLiteralToNumber("::", &ip));
+ CHECK(net::ParseIPLiteralToNumber("::1", &ip));
server_address_ = IPEndPoint(ip, server_address_.port());
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, SeparateFinPacket) {
@@ -247,7 +253,7 @@ TEST_F(EndToEndTest, SeparateFinPacket) {
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
request.AddBody("foo", true);
@@ -255,7 +261,7 @@ TEST_F(EndToEndTest, SeparateFinPacket) {
client_->SendData(std::string(), true);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, MultipleRequestResponse) {
@@ -268,9 +274,9 @@ TEST_F(EndToEndTest, MultipleRequestResponse) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, MultipleClients) {
@@ -294,12 +300,12 @@ TEST_F(EndToEndTest, MultipleClients) {
client_->SendData("bar", true);
client_->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client_->response_body());
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
client2->SendData("eep", true);
client2->WaitForResponse();
EXPECT_EQ(kFooResponseBody, client2->response_body());
- EXPECT_EQ(200ul, client2->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client2->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, RequestOverMultiplePackets) {
@@ -329,7 +335,7 @@ TEST_F(EndToEndTest, RequestOverMultiplePackets) {
// Make sure our request is too large to fit in one packet.
EXPECT_GT(strlen(kLargeRequest), min_payload_size);
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, MultipleFramesRandomOrder) {
@@ -360,7 +366,7 @@ TEST_F(EndToEndTest, MultipleFramesRandomOrder) {
// Make sure our request is too large to fit in one packet.
EXPECT_GT(strlen(kLargeRequest), min_payload_size);
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, PostMissingBytes) {
@@ -382,7 +388,7 @@ TEST_F(EndToEndTest, PostMissingBytes) {
// triggering an error response.
client_->SendCustomSynchronousRequest(request);
EXPECT_EQ("bad", client_->response_body());
- EXPECT_EQ(500ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
}
TEST_F(EndToEndTest, LargePost) {
@@ -460,7 +466,7 @@ TEST_F(EndToEndTest, InvalidStream) {
request.AddBody(body, true);
// Force the client to write with a stream ID belonging to a nonexistant
// server-side stream.
- QuicSessionPeer::SetNextStreamId(2, client_->client()->session());
+ QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2);
client_->SendCustomSynchronousRequest(request);
// EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
@@ -505,6 +511,7 @@ TEST_F(EndToEndTest, MultipleTermination) {
/*TEST_F(EndToEndTest, Timeout) {
config_.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromMicroseconds(500),
QuicTime::Delta::FromMicroseconds(500));
// Note: we do NOT ASSERT_TRUE: we may time out during initial handshake:
// that's enough to validate timeout in this case.
@@ -524,10 +531,58 @@ TEST_F(EndToEndTest, ResetConnection) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
client_->ResetConnection();
EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
- EXPECT_EQ(200ul, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+class WrongAddressWriter : public QuicPacketWriter {
+ public:
+ explicit WrongAddressWriter(int fd) : fd_(fd) {
+ IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("127.0.0.2", &ip));
+ self_address_ = IPEndPoint(ip, 0);
+ }
+
+ virtual int WritePacket(const char* buffer, size_t buf_len,
+ const IPAddressNumber& real_self_address,
+ const IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* blocked_writer,
+ int* error) OVERRIDE {
+ return QuicSocketUtils::WritePacket(fd_, buffer, buf_len,
+ self_address_.address(), peer_address,
+ error);
+ }
+
+ IPEndPoint self_address_;
+ int fd_;
+};
+
+TEST_F(EndToEndTest, ConnectionMigration) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128GcmEncrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ WrongAddressWriter writer(QuicClientPeer::GetFd(client_->client()));
+ QuicEpollConnectionHelper* helper =
+ reinterpret_cast<QuicEpollConnectionHelper*>(
+ QuicConnectionPeer::GetHelper(
+ client_->client()->session()->connection()));
+ QuicEpollConnectionHelperPeer::SetWriter(helper, &writer);
+
+ client_->SendSynchronousRequest("/bar");
+ QuicEpollConnectionHelperPeer::SetWriter(helper, NULL);
+
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error());
}
} // namespace
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index 0206c3f..9ca70a3 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -63,6 +63,8 @@ QuicClient::~QuicClient() {
}
bool QuicClient::Initialize() {
+ DCHECK(!initialized_);
+
epoll_server_.set_timeout_in_us(50 * 1000);
crypto_config_.SetDefaults();
int address_family = server_address_.GetSockAddrFamily();
@@ -167,6 +169,7 @@ void QuicClient::Disconnect() {
epoll_server_.UnregisterFD(fd_);
close(fd_);
fd_ = -1;
+ initialized_ = false;
}
void QuicClient::SendRequestsAndWaitForResponse(int argc, char *argv[]) {
diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h
index 9cfaa3e..0ce9104 100644
--- a/net/tools/quic/quic_client.h
+++ b/net/tools/quic/quic_client.h
@@ -24,6 +24,10 @@
namespace net {
namespace tools {
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
class QuicClient : public EpollCallbackInterface {
public:
QuicClient(IPEndPoint server_address, const std::string& server_hostname);
@@ -108,6 +112,8 @@ class QuicClient : public EpollCallbackInterface {
int fd() { return fd_; }
private:
+ friend class net::tools::test::QuicClientPeer;
+
// Read a UDP packet and hand it to the framer.
bool ReadAndProcessPacket();
diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_client_session.cc
index e3394d3..c41df67 100644
--- a/net/tools/quic/quic_client_session.cc
+++ b/net/tools/quic/quic_client_session.cc
@@ -19,8 +19,8 @@ QuicClientSession::QuicClientSession(
const QuicConfig& config,
QuicConnection* connection,
QuicCryptoClientConfig* crypto_config)
- : QuicSession(connection, false),
- crypto_stream_(server_hostname, config, this, crypto_config) {
+ : QuicSession(connection, config, false),
+ crypto_stream_(server_hostname, this, crypto_config) {
}
QuicClientSession::~QuicClientSession() {
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc
index 39543db..0b9603c 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_client_session_test.cc
@@ -27,22 +27,23 @@ class QuicClientSessionTest : public ::testing::Test {
protected:
QuicClientSessionTest()
: guid_(1),
- connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)),
- session_(kServerHostname, config_, connection_, &crypto_config_) {
- config_.SetDefaults();
+ connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)) {
crypto_config_.SetDefaults();
+ session_.reset(new QuicClientSession(kServerHostname, QuicConfig(),
+ connection_, &crypto_config_));
+ session_->config()->SetDefaults();
+ session_->config()->set_max_streams_per_connection(1, 1);
}
void CompleteCryptoHandshake() {
- ASSERT_TRUE(session_.CryptoConnect());
+ ASSERT_TRUE(session_->CryptoConnect());
CryptoTestUtils::HandshakeWithFakeServer(
- connection_, session_.GetCryptoStream());
+ connection_, session_->GetCryptoStream());
}
QuicGuid guid_;
PacketSavingConnection* connection_;
- QuicClientSession session_;
- QuicConfig config_;
+ scoped_ptr<QuicClientSession> session_;
QuicCryptoClientConfig crypto_config_;
};
@@ -56,28 +57,28 @@ TEST_F(QuicClientSessionTest, DISABLED_MaxNumConnections) {
CompleteCryptoHandshake();
QuicReliableClientStream* stream =
- session_.CreateOutgoingReliableStream();
+ session_->CreateOutgoingReliableStream();
ASSERT_TRUE(stream);
- EXPECT_FALSE(session_.CreateOutgoingReliableStream());
+ EXPECT_FALSE(session_->CreateOutgoingReliableStream());
// Close a stream and ensure I can now open a new one.
- session_.CloseStream(stream->id());
- stream = session_.CreateOutgoingReliableStream();
+ session_->CloseStream(stream->id());
+ stream = session_->CreateOutgoingReliableStream();
EXPECT_TRUE(stream);
}
TEST_F(QuicClientSessionTest, GoAwayReceived) {
// Initialize crypto before the client session will create a stream.
- ASSERT_TRUE(session_.CryptoConnect());
+ ASSERT_TRUE(session_->CryptoConnect());
// Simulate the server crypto handshake.
CryptoHandshakeMessage server_message;
server_message.set_tag(kSHLO);
- session_.GetCryptoStream()->OnHandshakeMessage(server_message);
+ session_->GetCryptoStream()->OnHandshakeMessage(server_message);
// After receiving a GoAway, I should no longer be able to create outgoing
// streams.
- session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
- EXPECT_EQ(NULL, session_.CreateOutgoingReliableStream());
+ session_->OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
+ EXPECT_EQ(NULL, session_->CreateOutgoingReliableStream());
}
} // namespace
diff --git a/net/tools/quic/quic_epoll_connection_helper.h b/net/tools/quic/quic_epoll_connection_helper.h
index edf5615..9ddef2f 100644
--- a/net/tools/quic/quic_epoll_connection_helper.h
+++ b/net/tools/quic/quic_epoll_connection_helper.h
@@ -30,6 +30,10 @@ class RetransmissionAlarm;
class SendAlarm;
class TimeoutAlarm;
+namespace test {
+class QuicEpollConnectionHelperPeer;
+} // namespace test
+
class QuicEpollConnectionHelper : public QuicConnectionHelperInterface {
public:
QuicEpollConnectionHelper(int fd, EpollServer* eps);
@@ -56,6 +60,7 @@ class QuicEpollConnectionHelper : public QuicConnectionHelperInterface {
private:
friend class QuicConnectionPeer;
+ friend class net::tools::test::QuicEpollConnectionHelperPeer;
QuicPacketWriter* writer_; // Not owned
EpollServer* epoll_server_; // Not owned.
diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc
index 91fafa0..5f5dc1b 100644
--- a/net/tools/quic/quic_epoll_connection_helper_test.cc
+++ b/net/tools/quic/quic_epoll_connection_helper_test.cc
@@ -29,7 +29,7 @@ namespace test {
namespace {
const char data1[] = "foo";
-const bool kHasData = true;
+const bool kFromPeer = true;
class TestConnectionHelper : public QuicEpollConnectionHelper {
public:
@@ -141,17 +141,18 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
- EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kHasData));
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
epoll_server_.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(connection_.connected());
- EXPECT_EQ(kDefaultTimeoutUs, epoll_server_.NowInUsec());
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec());
}
TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
EXPECT_TRUE(connection_.connected());
EXPECT_EQ(0, epoll_server_.NowInUsec());
- // When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
+ // When we send a packet, the timeout will change to 5000 +
+ // kDefaultInitialTimeoutSecs.
epoll_server_.AdvanceBy(5000);
EXPECT_EQ(5000, epoll_server_.NowInUsec());
@@ -162,13 +163,14 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// The original alarm will fire. We should not time out because we had a
// network event at t=5000. The alarm will reregister.
epoll_server_.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(kDefaultTimeoutUs, epoll_server_.NowInUsec());
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec());
// This time, we should time out.
- EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION));
epoll_server_.WaitForEventsAndExecuteCallbacks();
- EXPECT_EQ(kDefaultTimeoutUs + 5000, epoll_server_.NowInUsec());
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
+ epoll_server_.NowInUsec());
EXPECT_FALSE(connection_.connected());
}
diff --git a/net/tools/quic/quic_reliable_client_stream.h b/net/tools/quic/quic_reliable_client_stream.h
index c46420b..10b60c2 100644
--- a/net/tools/quic/quic_reliable_client_stream.h
+++ b/net/tools/quic/quic_reliable_client_stream.h
@@ -49,8 +49,6 @@ class QuicReliableClientStream : public ReliableQuicStream {
// Returns whatever headers have been received for this stream.
const BalsaHeaders& headers() { return headers_; }
- bool closed() { return closed_; }
-
protected:
std::string* mutable_data() { return &data_; }
BalsaHeaders* mutable_headers() { return &headers_; }
@@ -58,7 +56,6 @@ class QuicReliableClientStream : public ReliableQuicStream {
private:
BalsaHeaders headers_;
std::string data_;
- bool closed_;
DISALLOW_COPY_AND_ASSIGN(QuicReliableClientStream);
};
diff --git a/net/tools/quic/quic_reliable_client_stream_test.cc b/net/tools/quic/quic_reliable_client_stream_test.cc
index 8020c33..1f1eb54 100644
--- a/net/tools/quic/quic_reliable_client_stream_test.cc
+++ b/net/tools/quic/quic_reliable_client_stream_test.cc
@@ -25,11 +25,11 @@ namespace {
class QuicClientStreamTest : public ::testing::Test {
public:
QuicClientStreamTest()
- : session_("localhost", config_,
+ : session_("localhost", QuicConfig(),
new MockConnection(1, IPEndPoint(), 0, &eps_, false),
&crypto_config_),
body_("hello world") {
- config_.SetDefaults();
+ session_.config()->SetDefaults();
crypto_config_.SetDefaults();
headers_.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "Ok");
@@ -45,7 +45,6 @@ class QuicClientStreamTest : public ::testing::Test {
BalsaHeaders headers_;
string headers_string_;
string body_;
- QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
};
diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc
index 7319c67..a92c4ba 100644
--- a/net/tools/quic/quic_server.cc
+++ b/net/tools/quic/quic_server.cc
@@ -46,19 +46,12 @@ QuicServer::QuicServer()
// Use hardcoded crypto parameters for now.
config_.SetDefaults();
- CryptoHandshakeMessage extra_tags;
- config_.ToHandshakeMessage(&extra_tags);
QuicEpollClock clock(&epoll_server_);
scoped_ptr<CryptoHandshakeMessage> scfg(
crypto_config_.AddDefaultConfig(
- QuicRandom::GetInstance(), &clock, extra_tags,
+ QuicRandom::GetInstance(), &clock,
QuicCryptoServerConfig::kDefaultExpiry));
- // If we were using the same config in many servers then we would have to
- // parse a QuicConfig from config_tags here.
- if (!config_.SetFromHandshakeMessage(*scfg)) {
- CHECK(false) << "Crypto config could not be parsed by QuicConfig.";
- }
}
QuicServer::~QuicServer() {
diff --git a/net/tools/quic/quic_server_session.cc b/net/tools/quic/quic_server_session.cc
index f692332..330c307 100644
--- a/net/tools/quic/quic_server_session.cc
+++ b/net/tools/quic/quic_server_session.cc
@@ -16,9 +16,10 @@ QuicServerSession::QuicServerSession(
const QuicCryptoServerConfig& crypto_config,
QuicConnection* connection,
QuicSessionOwner* owner)
- : QuicSession(connection, true),
- crypto_stream_(config, crypto_config, this),
+ : QuicSession(connection, config, true),
+ crypto_stream_(crypto_config, this),
owner_(owner) {
+ set_max_open_streams(config.max_streams_per_connection());
}
QuicServerSession::~QuicServerSession() {
diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc
index 1798b04..329217a 100644
--- a/net/tools/quic/quic_socket_utils.cc
+++ b/net/tools/quic/quic_socket_utils.cc
@@ -149,7 +149,7 @@ int QuicSocketUtils::WritePacket(int fd, const char* buffer, size_t buf_len,
if (self_address.empty()) {
hdr.msg_control = 0;
hdr.msg_controllen = 0;
- } else if (self_address.size() == sizeof(sockaddr_in)) {
+ } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) {
cmsghdr *cmsg = reinterpret_cast<cmsghdr*>(cbuf);
hdr.msg_control = cmsg;
hdr.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo));
diff --git a/net/tools/quic/test_tools/quic_client_peer.cc b/net/tools/quic/test_tools/quic_client_peer.cc
new file mode 100644
index 0000000..8583594
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_client_peer.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 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/tools/quic/test_tools/quic_client_peer.h"
+
+#include "net/tools/quic/quic_client.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+// static
+void QuicClientPeer::Reinitialize(QuicClient* client) {
+ client->initialized_ = false;
+ client->epoll_server_.UnregisterFD(client->fd_);
+ client->Initialize();
+}
+
+// static
+int QuicClientPeer::GetFd(QuicClient* client) {
+ return client->fd_;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/net/tools/quic/test_tools/quic_client_peer.h b/net/tools/quic/test_tools/quic_client_peer.h
new file mode 100644
index 0000000..8eaa17e
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_client_peer.h
@@ -0,0 +1,25 @@
+// Copyright (c) 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_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+
+namespace net {
+namespace tools {
+
+class QuicClient;
+
+namespace test {
+
+class QuicClientPeer {
+ public:
+ static void Reinitialize(QuicClient* client);
+ static int GetFd(QuicClient* client);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
diff --git a/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc
new file mode 100644
index 0000000..e358273
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 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/tools/quic/test_tools/quic_epoll_connection_helper_peer.h"
+
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+// static
+void QuicEpollConnectionHelperPeer::SetWriter(QuicEpollConnectionHelper* helper,
+ QuicPacketWriter* writer) {
+ helper->writer_ = writer;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h
new file mode 100644
index 0000000..72085df
--- /dev/null
+++ b/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 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_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
+
+#include "base/basictypes.h"
+
+namespace net {
+namespace tools {
+
+class QuicPacketWriter;
+class QuicEpollConnectionHelper;
+
+namespace test {
+
+class QuicEpollConnectionHelperPeer {
+ public:
+ static void SetWriter(QuicEpollConnectionHelper* helper,
+ QuicPacketWriter* writer);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicEpollConnectionHelperPeer);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index c6c921e8..1a7539f 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -182,6 +182,9 @@ size_t QuicTestClient::bytes_written() const {
}
void QuicTestClient::OnClose(ReliableQuicStream* stream) {
+ if (stream_ != stream) {
+ return;
+ }
response_ = stream_->data();
headers_.CopyFrom(stream_->headers());
stream_error_ = stream_->stream_error();
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index a5c22ea6..05c3a57 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -54,8 +54,14 @@ bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
return true;
}
-TestSession::TestSession(QuicConnection* connection, bool is_server)
- : QuicSession(connection, is_server),
+void TestDecompressorVisitor::OnDecompressionError() {
+ error_ = true;
+}
+
+TestSession::TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server)
+ : QuicSession(connection, config, is_server),
crypto_stream_(NULL) {
}
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index fd69a1d..68f4de4 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -75,16 +75,21 @@ class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
public:
virtual ~TestDecompressorVisitor() {}
virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
+ virtual void OnDecompressionError() OVERRIDE;
std::string data() { return data_; }
+ bool error() { return error_; }
private:
std::string data_;
+ bool error_;
};
class TestSession : public QuicSession {
public:
- TestSession(QuicConnection* connection, bool is_server);
+ TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server);
virtual ~TestSession();
MOCK_METHOD1(CreateIncomingReliableStream,