summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-09-19 22:36:51 +0100
committerTorne (Richard Coles) <torne@google.com>2013-09-19 22:36:51 +0100
commitd0247b1b59f9c528cb6df88b4f2b9afaf80d181e (patch)
tree5c397fadc190cc71bffe2ffad1efc27a5b95309d /net
parentf7571f5f07547e2f3e0addf48d1f2a7ec3632957 (diff)
downloadchromium_org-d0247b1b59f9c528cb6df88b4f2b9afaf80d181e.tar.gz
Merge from Chromium at DEPS revision 224184
This commit was generated by merge_to_master.py. Change-Id: Ia3424df5abed9bea642c522b9e2358dceabd8423
Diffstat (limited to 'net')
-rw-r--r--net/android/keystore_openssl.cc152
-rw-r--r--net/base/address_family.h2
-rw-r--r--net/base/cache_type.h3
-rw-r--r--net/base/file_stream_context.cc5
-rw-r--r--net/base/file_stream_unittest.cc58
-rw-r--r--net/base/linked_hash_map.h12
-rw-r--r--net/base/net_log_event_type_list.h16
-rw-r--r--net/base/upload_data_stream_unittest.cc8
-rw-r--r--net/base/upload_file_element_reader_unittest.cc9
-rw-r--r--net/cert/cert_verify_proc.cc2
-rw-r--r--net/cert_verify_result_android_java.target.darwin-arm.mk4
-rw-r--r--net/cert_verify_result_android_java.target.darwin-mips.mk4
-rw-r--r--net/cert_verify_result_android_java.target.darwin-x86.mk4
-rw-r--r--net/cert_verify_result_android_java.target.linux-arm.mk4
-rw-r--r--net/cert_verify_result_android_java.target.linux-mips.mk4
-rw-r--r--net/cert_verify_result_android_java.target.linux-x86.mk4
-rw-r--r--net/certificate_mime_types_java.target.darwin-arm.mk4
-rw-r--r--net/certificate_mime_types_java.target.darwin-mips.mk4
-rw-r--r--net/certificate_mime_types_java.target.darwin-x86.mk4
-rw-r--r--net/certificate_mime_types_java.target.linux-arm.mk4
-rw-r--r--net/certificate_mime_types_java.target.linux-mips.mk4
-rw-r--r--net/certificate_mime_types_java.target.linux-x86.mk4
-rw-r--r--net/cookies/cookie_monster_perftest.cc28
-rw-r--r--net/data/url_request_unittest/redirect-test.html.mock-http-headers1
-rw-r--r--net/disk_cache/backend_impl.cc2
-rw-r--r--net/disk_cache/backend_unittest.cc63
-rw-r--r--net/disk_cache/block_files.cc127
-rw-r--r--net/disk_cache/block_files.h35
-rw-r--r--net/disk_cache/disk_cache_perftest.cc12
-rw-r--r--net/disk_cache/disk_cache_test_base.cc5
-rw-r--r--net/disk_cache/disk_format_base.h1
-rw-r--r--net/disk_cache/entry_unittest.cc234
-rw-r--r--net/disk_cache/histogram_macros.h3
-rw-r--r--net/disk_cache/mem_entry_impl.h6
-rw-r--r--net/disk_cache/simple/simple_backend_impl.cc56
-rw-r--r--net/disk_cache/simple/simple_backend_version.h27
-rw-r--r--net/disk_cache/simple/simple_entry_format.h20
-rw-r--r--net/disk_cache/simple/simple_entry_format_history.h62
-rw-r--r--net/disk_cache/simple/simple_entry_impl.cc264
-rw-r--r--net/disk_cache/simple/simple_entry_impl.h56
-rw-r--r--net/disk_cache/simple/simple_index_file.cc268
-rw-r--r--net/disk_cache/simple/simple_index_file.h36
-rw-r--r--net/disk_cache/simple/simple_index_file_unittest.cc60
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.cc353
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.h69
-rw-r--r--net/disk_cache/simple/simple_test_util.cc2
-rw-r--r--net/disk_cache/simple/simple_util.cc18
-rw-r--r--net/disk_cache/simple/simple_util.h15
-rw-r--r--net/disk_cache/simple/simple_version_upgrade.cc203
-rw-r--r--net/disk_cache/simple/simple_version_upgrade.h50
-rw-r--r--net/disk_cache/simple/simple_version_upgrade_unittest.cc146
-rw-r--r--net/disk_cache/v3/block_bitmaps.cc310
-rw-r--r--net/disk_cache/v3/block_bitmaps.h58
-rw-r--r--net/disk_cache/v3/block_bitmaps_unittest.cc342
-rw-r--r--net/dns/dns_config_service.cc8
-rw-r--r--net/dns/dns_config_service.h5
-rw-r--r--net/dns/dns_config_service_win.cc194
-rw-r--r--net/dns/dns_config_service_win.h18
-rw-r--r--net/dns/dns_config_service_win_unittest.cc39
-rw-r--r--net/dns/host_resolver_impl.cc15
-rw-r--r--net/dns/host_resolver_impl.h4
-rw-r--r--net/dns/host_resolver_impl_unittest.cc18
-rw-r--r--net/dns/mdns_client.cc29
-rw-r--r--net/dns/mdns_client.h3
-rw-r--r--net/dns/mdns_client_impl.cc17
-rw-r--r--net/dns/mdns_client_impl.h2
-rw-r--r--net/dns/mock_host_resolver.cc6
-rw-r--r--net/http/http_cache.cc5
-rw-r--r--net/http/http_cache_transaction.cc13
-rw-r--r--net/http/http_cache_unittest.cc109
-rw-r--r--net/http/http_transaction.h3
-rw-r--r--net/http/transport_security_state_static.certs38
-rw-r--r--net/http/transport_security_state_static.h8
-rw-r--r--net/http/transport_security_state_static.json6
-rw-r--r--net/http_server.target.darwin-arm.mk4
-rw-r--r--net/http_server.target.darwin-mips.mk4
-rw-r--r--net/http_server.target.darwin-x86.mk4
-rw-r--r--net/http_server.target.linux-arm.mk4
-rw-r--r--net/http_server.target.linux-mips.mk4
-rw-r--r--net/http_server.target.linux-x86.mk4
-rw-r--r--net/net.gyp31
-rw-r--r--net/net.target.darwin-arm.mk12
-rw-r--r--net/net.target.darwin-mips.mk12
-rw-r--r--net/net.target.darwin-x86.mk12
-rw-r--r--net/net.target.linux-arm.mk12
-rw-r--r--net/net.target.linux-mips.mk12
-rw-r--r--net/net.target.linux-x86.mk12
-rw-r--r--net/net_errors_java.target.darwin-arm.mk4
-rw-r--r--net/net_errors_java.target.darwin-mips.mk4
-rw-r--r--net/net_errors_java.target.darwin-x86.mk4
-rw-r--r--net/net_errors_java.target.linux-arm.mk4
-rw-r--r--net/net_errors_java.target.linux-mips.mk4
-rw-r--r--net/net_errors_java.target.linux-x86.mk4
-rw-r--r--net/net_jni_headers.target.darwin-arm.mk4
-rw-r--r--net/net_jni_headers.target.darwin-mips.mk4
-rw-r--r--net/net_jni_headers.target.darwin-x86.mk4
-rw-r--r--net/net_jni_headers.target.linux-arm.mk4
-rw-r--r--net/net_jni_headers.target.linux-mips.mk4
-rw-r--r--net/net_jni_headers.target.linux-x86.mk4
-rw-r--r--net/private_key_types_java.target.darwin-arm.mk4
-rw-r--r--net/private_key_types_java.target.darwin-mips.mk4
-rw-r--r--net/private_key_types_java.target.darwin-x86.mk4
-rw-r--r--net/private_key_types_java.target.linux-arm.mk4
-rw-r--r--net/private_key_types_java.target.linux-mips.mk4
-rw-r--r--net/private_key_types_java.target.linux-x86.mk4
-rw-r--r--net/proxy/proxy_resolver_perftest.cc4
-rw-r--r--net/proxy/proxy_script_decider.cc77
-rw-r--r--net/proxy/proxy_script_decider.h15
-rw-r--r--net/proxy/proxy_script_decider_unittest.cc100
-rw-r--r--net/quic/congestion_control/fix_rate_sender.cc11
-rw-r--r--net/quic/congestion_control/fix_rate_sender.h10
-rw-r--r--net/quic/congestion_control/fix_rate_test.cc13
-rw-r--r--net/quic/congestion_control/inter_arrival_sender.cc11
-rw-r--r--net/quic/congestion_control/inter_arrival_sender.h10
-rw-r--r--net/quic/congestion_control/inter_arrival_sender_test.cc2
-rw-r--r--net/quic/congestion_control/quic_congestion_control_test.cc12
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.cc40
-rw-r--r--net/quic/congestion_control/quic_congestion_manager.h10
-rw-r--r--net/quic/congestion_control/quic_congestion_manager_test.cc39
-rw-r--r--net/quic/congestion_control/send_algorithm_interface.cc2
-rw-r--r--net/quic/congestion_control/send_algorithm_interface.h8
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender.cc13
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender.h10
-rw-r--r--net/quic/congestion_control/tcp_cubic_sender_test.cc19
-rw-r--r--net/quic/crypto/crypto_server_config.cc10
-rw-r--r--net/quic/crypto/crypto_server_config.h4
-rw-r--r--net/quic/crypto/crypto_server_test.cc8
-rw-r--r--net/quic/crypto/proof_source.h7
-rw-r--r--net/quic/crypto/proof_source_chromium.cc3
-rw-r--r--net/quic/crypto/proof_source_chromium.h3
-rw-r--r--net/quic/crypto/proof_test.cc161
-rw-r--r--net/quic/crypto/proof_verifier.h7
-rw-r--r--net/quic/crypto/proof_verifier_chromium.cc10
-rw-r--r--net/quic/crypto/proof_verifier_chromium.h6
-rw-r--r--net/quic/quic_client_session.cc6
-rw-r--r--net/quic/quic_client_session.h2
-rw-r--r--net/quic/quic_connection.cc528
-rw-r--r--net/quic/quic_connection.h104
-rw-r--r--net/quic/quic_connection_helper_test.cc67
-rw-r--r--net/quic/quic_connection_logger.cc9
-rw-r--r--net/quic/quic_connection_logger.h1
-rw-r--r--net/quic/quic_connection_test.cc453
-rw-r--r--net/quic/quic_crypto_client_stream.cc1
-rw-r--r--net/quic/quic_crypto_server_stream.cc1
-rw-r--r--net/quic/quic_crypto_stream.cc4
-rw-r--r--net/quic/quic_framer.cc2
-rw-r--r--net/quic/quic_http_stream.cc16
-rw-r--r--net/quic/quic_http_stream.h9
-rw-r--r--net/quic/quic_http_stream_test.cc89
-rw-r--r--net/quic/quic_http_utils.cc23
-rw-r--r--net/quic/quic_http_utils.h22
-rw-r--r--net/quic/quic_http_utils_test.cc35
-rw-r--r--net/quic/quic_network_transaction_unittest.cc4
-rw-r--r--net/quic/quic_packet_creator.cc34
-rw-r--r--net/quic/quic_packet_creator.h8
-rw-r--r--net/quic/quic_packet_creator_test.cc47
-rw-r--r--net/quic/quic_packet_generator.cc23
-rw-r--r--net/quic/quic_packet_generator.h25
-rw-r--r--net/quic/quic_packet_generator_test.cc66
-rw-r--r--net/quic/quic_protocol.cc8
-rw-r--r--net/quic/quic_protocol.h9
-rw-r--r--net/quic/quic_protocol_test.cc20
-rw-r--r--net/quic/quic_reliable_client_stream.cc8
-rw-r--r--net/quic/quic_reliable_client_stream.h8
-rw-r--r--net/quic/quic_reliable_client_stream_test.cc8
-rw-r--r--net/quic/quic_sent_packet_manager.cc221
-rw-r--r--net/quic/quic_sent_packet_manager.h140
-rw-r--r--net/quic/quic_sent_packet_manager_test.cc80
-rw-r--r--net/quic/quic_session.cc77
-rw-r--r--net/quic/quic_session.h25
-rw-r--r--net/quic/quic_session_test.cc102
-rw-r--r--net/quic/quic_stream_factory.cc29
-rw-r--r--net/quic/quic_stream_factory_test.cc2
-rw-r--r--net/quic/quic_stream_sequencer.cc24
-rw-r--r--net/quic/quic_stream_sequencer_test.cc6
-rw-r--r--net/quic/quic_utils.cc4
-rw-r--r--net/quic/reliable_quic_stream.cc48
-rw-r--r--net/quic/reliable_quic_stream.h21
-rw-r--r--net/quic/reliable_quic_stream_test.cc61
-rw-r--r--net/quic/test_tools/quic_connection_peer.cc8
-rw-r--r--net/quic/test_tools/quic_test_utils.cc7
-rw-r--r--net/quic/test_tools/quic_test_utils.h29
-rw-r--r--net/socket/tcp_client_socket.cc319
-rw-r--r--net/socket/tcp_client_socket.h124
-rw-r--r--net/socket/tcp_client_socket_libevent.cc830
-rw-r--r--net/socket/tcp_client_socket_libevent.h256
-rw-r--r--net/socket/tcp_client_socket_win.cc956
-rw-r--r--net/socket/tcp_client_socket_win.h157
-rw-r--r--net/socket/tcp_server_socket.cc24
-rw-r--r--net/socket/tcp_socket.cc59
-rw-r--r--net/socket/tcp_socket.h8
-rw-r--r--net/socket/tcp_socket_libevent.cc706
-rw-r--r--net/socket/tcp_socket_libevent.h193
-rw-r--r--net/socket/tcp_socket_unittest.cc69
-rw-r--r--net/socket/tcp_socket_win.cc821
-rw-r--r--net/socket/tcp_socket_win.h98
-rw-r--r--net/socket/transport_client_socket_pool.cc5
-rw-r--r--net/ssl/ssl_cipher_suite_names.cc7
-rw-r--r--net/test/python_utils.cc12
-rw-r--r--net/test/spawned_test_server/local_test_server_win.cc69
-rw-r--r--net/third_party/nss/README.chromium4
-rwxr-xr-xnet/third_party/nss/patches/applypatches.sh2
-rw-r--r--net/third_party/nss/patches/chacha20poly1305.patch280
-rw-r--r--net/third_party/nss/ssl/ssl3con.c69
-rw-r--r--net/third_party/nss/ssl/ssl3ecc.c4
-rw-r--r--net/third_party/nss/ssl/sslenum.c2
-rw-r--r--net/third_party/nss/ssl/sslimpl.h4
-rw-r--r--net/third_party/nss/ssl/sslinfo.c3
-rw-r--r--net/third_party/nss/ssl/sslproto.h3
-rw-r--r--net/third_party/nss/ssl/sslsock.c2
-rw-r--r--net/third_party/nss/ssl/sslt.h3
-rw-r--r--net/tools/quic/end_to_end_test.cc28
-rw-r--r--net/tools/quic/quic_epoll_connection_helper_test.cc41
-rw-r--r--net/tools/quic/quic_reliable_server_stream_test.cc35
-rw-r--r--net/tools/quic/quic_server_session.h6
-rw-r--r--net/tools/quic/quic_server_session_test.cc255
-rw-r--r--net/tools/quic/quic_spdy_client_stream.h4
-rw-r--r--net/tools/quic/quic_time_wait_list_manager_test.cc397
-rw-r--r--net/tools/quic/test_tools/quic_test_client.cc10
-rw-r--r--net/tools/quic/test_tools/quic_test_client.h4
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.cc13
-rw-r--r--net/tools/quic/test_tools/quic_test_utils.h22
-rwxr-xr-xnet/tools/testserver/testserver.py4
-rw-r--r--net/url_request/test_url_fetcher_factory.cc11
-rw-r--r--net/url_request/test_url_fetcher_factory.h6
-rw-r--r--net/url_request/url_request_job.cc4
-rw-r--r--net/url_request/url_request_job_unittest.cc37
-rw-r--r--net/websockets/README5
-rw-r--r--net/websockets/websocket_extension.cc43
-rw-r--r--net/websockets/websocket_extension.h57
-rw-r--r--net/websockets/websocket_extension_parser.cc158
-rw-r--r--net/websockets/websocket_extension_parser.h59
-rw-r--r--net/websockets/websocket_extension_parser_test.cc122
233 files changed, 8600 insertions, 4945 deletions
diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc
index cd55ece333..5ad847344a 100644
--- a/net/android/keystore_openssl.cc
+++ b/net/android/keystore_openssl.cc
@@ -35,7 +35,7 @@
//
// Generally speaking, OpenSSL provides many different ways to sign
// digests. This code doesn't support all these cases, only the ones that
-// are required to sign the MAC during the OpenSSL handshake for TLS.
+// are required to sign the digest during the OpenSSL handshake for TLS.
//
// The OpenSSL EVP_PKEY type is a generic wrapper around key pairs.
// Internally, it can hold a pointer to a RSA, DSA or ECDSA structure,
@@ -106,7 +106,6 @@ typedef crypto::ScopedOpenSSL<RSA, RSA_free> ScopedRSA;
typedef crypto::ScopedOpenSSL<DSA, DSA_free> ScopedDSA;
typedef crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ScopedEC_KEY;
typedef crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free> ScopedEC_GROUP;
-typedef crypto::ScopedOpenSSL<X509_SIG, X509_SIG_free> ScopedX509_SIG;
// Custom RSA_METHOD that uses the platform APIs.
// Note that for now, only signing through RSA_sign() is really supported.
@@ -133,14 +132,60 @@ int RsaMethodPubDec(int flen,
return -1;
}
+// See RSA_eay_private_encrypt in
+// third_party/openssl/openssl/crypto/rsa/rsa_eay.c for the default
+// implementation of this function.
int RsaMethodPrivEnc(int flen,
const unsigned char *from,
unsigned char *to,
RSA *rsa,
int padding) {
- NOTIMPLEMENTED();
- RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
- return -1;
+ DCHECK_EQ(RSA_PKCS1_PADDING, padding);
+ if (padding != RSA_PKCS1_PADDING) {
+ // TODO(davidben): If we need to, we can implement RSA_NO_PADDING
+ // by using javax.crypto.Cipher and picking either the
+ // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as
+ // appropriate. I believe support for both of these was added in
+ // the same Android version as the "NONEwithRSA"
+ // java.security.Signature algorithm, so the same version checks
+ // for GetRsaLegacyKey should work.
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ return -1;
+ }
+
+ // Retrieve private key JNI reference.
+ jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
+ if (!private_key) {
+ LOG(WARNING) << "Null JNI reference passed to RsaMethodPrivEnc!";
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ base::StringPiece from_piece(reinterpret_cast<const char*>(from), flen);
+ std::vector<uint8> result;
+ // For RSA keys, this function behaves as RSA_private_encrypt with
+ // PKCS#1 padding.
+ if (!RawSignDigestWithPrivateKey(private_key, from_piece, &result)) {
+ LOG(WARNING) << "Could not sign message in RsaMethodPrivEnc!";
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ size_t expected_size = static_cast<size_t>(RSA_size(rsa));
+ if (result.size() > expected_size) {
+ LOG(ERROR) << "RSA Signature size mismatch, actual: "
+ << result.size() << ", expected <= " << expected_size;
+ RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey
+ // should pad with leading 0s, but if it doesn't, pad the result.
+ size_t zero_pad = expected_size - result.size();
+ memset(to, 0, zero_pad);
+ memcpy(to + zero_pad, &result[0], result.size());
+
+ return expected_size;
}
int RsaMethodPrivDec(int flen,
@@ -154,8 +199,6 @@ int RsaMethodPrivDec(int flen,
}
int RsaMethodInit(RSA* rsa) {
- // Required to ensure that RsaMethodSign will be called.
- rsa->flags |= RSA_FLAG_SIGN_VER;
return 0;
}
@@ -173,99 +216,6 @@ int RsaMethodFinish(RSA* rsa) {
return 0;
}
-// Although these parameters are, per OpenSSL, named |message| and
-// |message_len|, RsaMethodSign is actually passed a message digest,
-// not the original message.
-int RsaMethodSign(int type,
- const unsigned char* message,
- unsigned int message_len,
- unsigned char* signature,
- unsigned int* signature_len,
- const RSA* rsa) {
- // Retrieve private key JNI reference.
- jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
- if (!private_key) {
- LOG(WARNING) << "Null JNI reference passed to RsaMethodSign!";
- return 0;
- }
-
- // See RSA_sign in third_party/openssl/openssl/crypto/rsa/rsa_sign.c.
- base::StringPiece message_piece;
- std::vector<uint8> buffer; // To store |message| wrapped in a DigestInfo.
- if (type == NID_md5_sha1) {
- // For TLS < 1.2, sign just |message|.
- message_piece.set(message, static_cast<size_t>(message_len));
- } else {
- // For TLS 1.2, wrap |message| in a PKCS #1 DigestInfo before signing.
- ScopedX509_SIG sig(X509_SIG_new());
- if (!sig.get())
- return 0;
- if (X509_ALGOR_set0(sig.get()->algor,
- OBJ_nid2obj(type), V_ASN1_NULL, 0) != 1) {
- return 0;
- }
- if (sig.get()->algor->algorithm == NULL) {
- RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
- return 0;
- }
- if (sig.get()->algor->algorithm->length == 0) {
- RSAerr(RSA_F_RSA_SIGN,
- RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD);
- return 0;
- }
- if (ASN1_OCTET_STRING_set(sig.get()->digest, message, message_len) != 1)
- return 0;
-
- int len = i2d_X509_SIG(sig.get(), NULL);
- if (len < 0) {
- LOG(WARNING) << "Couldn't encode X509_SIG structure";
- return 0;
- }
- buffer.resize(len);
- // OpenSSL takes a pointer to a pointer so it can kindly increment
- // it for you.
- unsigned char* p = &buffer[0];
- len = i2d_X509_SIG(sig.get(), &p);
- if (len < 0) {
- LOG(WARNING) << "Couldn't encode X509_SIG structure";
- return 0;
- }
-
- message_piece.set(&buffer[0], static_cast<size_t>(len));
- }
-
- // Sanity-check the size.
- //
- // TODO(davidben): Do we need to do this? OpenSSL does, but
- // RawSignDigestWithPrivateKey does error on sufficiently large
- // input. However, it doesn't take the padding into account.
- size_t expected_size = static_cast<size_t>(RSA_size(rsa));
- if (message_piece.size() > expected_size - RSA_PKCS1_PADDING_SIZE) {
- RSAerr(RSA_F_RSA_SIGN, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
- return 0;
- }
-
- // Sign |message_piece| with the private key through JNI.
- std::vector<uint8> result;
-
- if (!RawSignDigestWithPrivateKey(
- private_key, message_piece, &result)) {
- LOG(WARNING) << "Could not sign message in RsaMethodSign!";
- return 0;
- }
-
- if (result.size() > expected_size) {
- LOG(ERROR) << "RSA Signature size mismatch, actual: "
- << result.size() << ", expected <= " << expected_size;
- return 0;
- }
-
- // Copy result to OpenSSL-provided buffer
- memcpy(signature, &result[0], result.size());
- *signature_len = static_cast<unsigned int>(result.size());
- return 1;
-}
-
const RSA_METHOD android_rsa_method = {
/* .name = */ "Android signing-only RSA method",
/* .rsa_pub_enc = */ RsaMethodPubEnc,
@@ -281,7 +231,7 @@ const RSA_METHOD android_rsa_method = {
// it's not valid for the certificate.
/* .flags = */ RSA_METHOD_FLAG_NO_CHECK,
/* .app_data = */ NULL,
- /* .rsa_sign = */ RsaMethodSign,
+ /* .rsa_sign = */ NULL,
/* .rsa_verify = */ NULL,
/* .rsa_keygen = */ NULL,
};
diff --git a/net/base/address_family.h b/net/base/address_family.h
index 75beb29d64..07adfaa2f4 100644
--- a/net/base/address_family.h
+++ b/net/base/address_family.h
@@ -24,6 +24,8 @@ enum {
HOST_RESOLVER_LOOPBACK_ONLY = 1 << 1,
// Indicate the address family was set because no IPv6 support was detected.
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6 = 1 << 2,
+ // The resolver should only invoke getaddrinfo, not DnsClient.
+ HOST_RESOLVER_SYSTEM_ONLY = 1 << 3
};
typedef int HostResolverFlags;
diff --git a/net/base/cache_type.h b/net/base/cache_type.h
index 69b5646ece..06aa826b8f 100644
--- a/net/base/cache_type.h
+++ b/net/base/cache_type.h
@@ -13,7 +13,8 @@ enum CacheType {
MEMORY_CACHE, // Data is stored only in memory.
MEDIA_CACHE, // Optimized to handle media files.
APP_CACHE, // Backing store for an AppCache.
- SHADER_CACHE // Backing store for the GL shader cache.
+ SHADER_CACHE, // Backing store for the GL shader cache.
+ PNACL_CACHE, // Backing store the PNaCl translation cache
};
// The types of disk cache backend, only used at backend instantiation.
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc
index 0d4f8d9b69..2e77475204 100644
--- a/net/base/file_stream_context.cc
+++ b/net/base/file_stream_context.cc
@@ -174,11 +174,6 @@ void FileStream::Context::RecordError(const IOResult& result,
return;
}
- // The following check is against incorrect use or bug. File descriptor
- // shouldn't ever be closed outside of FileStream while it still tries to do
- // something with it.
- DCHECK_NE(result.result, ERR_INVALID_HANDLE);
-
if (!orphaned_) {
bound_net_log_.AddEvent(
NetLog::TYPE_FILE_STREAM_ERROR,
diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc
index addae890b5..c76f3d939a 100644
--- a/net/base/file_stream_unittest.cc
+++ b/net/base/file_stream_unittest.cc
@@ -1121,42 +1121,56 @@ TEST_F(FileStreamTest, AsyncOpenAndDelete) {
// Verify that async Write() errors are mapped correctly.
TEST_F(FileStreamTest, AsyncWriteError) {
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
- base::PLATFORM_FILE_WRITE |
+ // Try opening file as read-only and then writing to it using FileStream.
+ base::PlatformFile file = base::CreatePlatformFile(
+ temp_file_path(),
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC,
+ NULL,
+ NULL);
+ ASSERT_NE(base::kInvalidPlatformFileValue, file);
+
+ int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, callback.WaitForResult());
+ scoped_ptr<FileStream> stream(
+ new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
- // Try passing NULL buffer to Write() and check that it fails.
- scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(NULL);
- rv = stream->Write(buf.get(), 1, callback.callback());
+ scoped_refptr<IOBuffer> buf = new IOBuffer(1);
+ buf->data()[0] = 0;
+
+ TestCompletionCallback callback;
+ int rv = stream->Write(buf.get(), 1, callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(rv, 0);
+
+ base::ClosePlatformFile(file);
}
// Verify that async Read() errors are mapped correctly.
TEST_F(FileStreamTest, AsyncReadError) {
- scoped_ptr<FileStream> stream(
- new FileStream(NULL, base::MessageLoopProxy::current()));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
+ // Try opening file for write and then reading from it using FileStream.
+ base::PlatformFile file = base::CreatePlatformFile(
+ temp_file_path(),
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_ASYNC,
+ NULL,
+ NULL);
+ ASSERT_NE(base::kInvalidPlatformFileValue, file);
+
+ int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, callback.WaitForResult());
+ scoped_ptr<FileStream> stream(
+ new FileStream(file, flags, NULL, base::MessageLoopProxy::current()));
- // Try passing NULL buffer to Read() and check that it fails.
- scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(NULL);
- rv = stream->Read(buf.get(), 1, callback.callback());
+ scoped_refptr<IOBuffer> buf = new IOBuffer(1);
+ TestCompletionCallback callback;
+ int rv = stream->Read(buf.get(), 1, callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(rv, 0);
+
+ base::ClosePlatformFile(file);
}
} // namespace
diff --git a/net/base/linked_hash_map.h b/net/base/linked_hash_map.h
index d08b68d259..7948647df0 100644
--- a/net/base/linked_hash_map.h
+++ b/net/base/linked_hash_map.h
@@ -146,7 +146,7 @@ class linked_hash_map {
std::pair<typename MapType::iterator, typename MapType::iterator> eq_range =
map_.equal_range(key);
- return make_pair(eq_range.first->second, eq_range.second->second);
+ return std::make_pair(eq_range.first->second, eq_range.second->second);
}
std::pair<const_iterator, const_iterator> equal_range(
@@ -159,13 +159,13 @@ class linked_hash_map {
const const_iterator& end_iter = eq_range.second != map_.end() ?
eq_range.second->second : end();
- return make_pair(start_iter, end_iter);
+ return std::make_pair(start_iter, end_iter);
}
// Returns the value mapped to key, or an inserted iterator to that position
// in the map.
Value& operator[](const key_type& key) {
- return (*((this->insert(make_pair(key, Value()))).first)).second;
+ return (*((this->insert(std::make_pair(key, Value()))).first)).second;
}
// Inserts an element into the map
@@ -174,7 +174,7 @@ class linked_hash_map {
// return a pair with an iterator to it, and false indicating that we
// didn't insert anything.
typename MapType::iterator found = map_.find(pair.first);
- if (found != map_.end()) return make_pair(found->second, false);
+ if (found != map_.end()) return std::make_pair(found->second, false);
// Otherwise, insert into the list first.
list_.push_back(pair);
@@ -184,10 +184,10 @@ class linked_hash_map {
typename ListType::iterator last = list_.end();
--last;
- CHECK(map_.insert(make_pair(pair.first, last)).second)
+ CHECK(map_.insert(std::make_pair(pair.first, last)).second)
<< "Map and list are inconsistent";
- return make_pair(last, true);
+ return std::make_pair(last, true);
}
size_type size() const {
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index 4c4371cc35..837de5f3ef 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -1460,6 +1460,12 @@ EVENT_TYPE(QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED)
// }
EVENT_TYPE(QUIC_SESSION_VERSION_NEGOTIATION_PACKET_RECEIVED)
+// Session sucessfully negotiated QUIC version number.
+// {
+// "version": <String of QUIC version negotiated with the server>,
+// }
+EVENT_TYPE(QUIC_SESSION_VERSION_NEGOTIATED)
+
// Session revived a QUIC packet packet via FEC.
// {
// "guid": <The 64-bit GUID for this connection, as a base-10 string>,
@@ -1632,14 +1638,8 @@ EVENT_TYPE(NETWORK_CHANGED)
// {
// "nameservers": <List of name server IPs>,
// "search": <List of domain suffixes>,
-// "unhandled_options": <See DnsConfig>,
-// "append_to_multi_label_name": <See DnsConfig>,
-// "ndots": <See DnsConfig>,
-// "timeout": <See DnsConfig>,
-// "attempts": <See DnsConfig>,
-// "rotate": <See DnsConfig>,
-// "edns0": <See DnsConfig>,
-// "num_hosts": <Number of entries in the HOSTS file>
+// "num_hosts": <Number of entries in the HOSTS file>,
+// <other>: <See DnsConfig>
// }
EVENT_TYPE(DNS_CONFIG_CHANGED)
diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc
index 42b71b9cbe..e0cbc27c10 100644
--- a/net/base/upload_data_stream_unittest.cc
+++ b/net/base/upload_data_stream_unittest.cc
@@ -14,6 +14,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/time/time.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -123,9 +124,14 @@ class MockUploadElementReader : public UploadElementReader {
class UploadDataStreamTest : public PlatformTest {
public:
- virtual void SetUp() OVERRIDE {
+ virtual void SetUp() {
+ PlatformTest::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
+ virtual ~UploadDataStreamTest() {
+ element_readers_.clear();
+ base::RunLoop().RunUntilIdle();
+ }
void FileChangedHelper(const base::FilePath& file_path,
const base::Time& time,
diff --git a/net/base/upload_file_element_reader_unittest.cc b/net/base/upload_file_element_reader_unittest.cc
index 8224f77304..b0435019e9 100644
--- a/net/base/upload_file_element_reader_unittest.cc
+++ b/net/base/upload_file_element_reader_unittest.cc
@@ -7,6 +7,7 @@
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
@@ -17,7 +18,8 @@ namespace net {
class UploadFileElementReaderTest : public PlatformTest {
protected:
- virtual void SetUp() OVERRIDE {
+ virtual void SetUp() {
+ PlatformTest::SetUp();
// Some tests (*.ReadPartially) rely on bytes_.size() being even.
const char kData[] = "123456789abcdefghi";
bytes_.assign(kData, kData + arraysize(kData) - 1);
@@ -44,6 +46,11 @@ class UploadFileElementReaderTest : public PlatformTest {
EXPECT_FALSE(reader_->IsInMemory());
}
+ virtual ~UploadFileElementReaderTest() {
+ reader_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
std::vector<char> bytes_;
scoped_ptr<UploadElementReader> reader_;
base::ScopedTempDir temp_dir_;
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index 3988de3c59..05f6c30b8f 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -374,7 +374,7 @@ bool CertVerifyProc::IsPublicKeyBlacklisted(
// in 2036, but we can probably remove in a couple of years (2014).
{0xd9, 0xf5, 0xc6, 0xce, 0x57, 0xff, 0xaa, 0x39, 0xcc, 0x7e,
0xd1, 0x72, 0xbd, 0x53, 0xe0, 0xd3, 0x07, 0x83, 0x4b, 0xd1},
- // Win32/Sirefef.gen!C generates fake certifciates with this public key.
+ // Win32/Sirefef.gen!C generates fake certificates with this public key.
{0xa4, 0xf5, 0x6e, 0x9e, 0x1d, 0x9a, 0x3b, 0x7b, 0x1a, 0xc3,
0x31, 0xcf, 0x64, 0xfc, 0x76, 0x2c, 0xd0, 0x51, 0xfb, 0xa4},
};
diff --git a/net/cert_verify_result_android_java.target.darwin-arm.mk b/net/cert_verify_result_android_java.target.darwin-arm.mk
index 72a2a06331..8b59c7e5f9 100644
--- a/net/cert_verify_result_android_java.target.darwin-arm.mk
+++ b/net/cert_verify_result_android_java.target.darwin-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cert_verify_result_android_java.target.darwin-mips.mk b/net/cert_verify_result_android_java.target.darwin-mips.mk
index 6461ce491f..e6479204b2 100644
--- a/net/cert_verify_result_android_java.target.darwin-mips.mk
+++ b/net/cert_verify_result_android_java.target.darwin-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cert_verify_result_android_java.target.darwin-x86.mk b/net/cert_verify_result_android_java.target.darwin-x86.mk
index 53ce61ed0d..f845bd663c 100644
--- a/net/cert_verify_result_android_java.target.darwin-x86.mk
+++ b/net/cert_verify_result_android_java.target.darwin-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cert_verify_result_android_java.target.linux-arm.mk b/net/cert_verify_result_android_java.target.linux-arm.mk
index 72a2a06331..8b59c7e5f9 100644
--- a/net/cert_verify_result_android_java.target.linux-arm.mk
+++ b/net/cert_verify_result_android_java.target.linux-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cert_verify_result_android_java.target.linux-mips.mk b/net/cert_verify_result_android_java.target.linux-mips.mk
index 6461ce491f..e6479204b2 100644
--- a/net/cert_verify_result_android_java.target.linux-mips.mk
+++ b/net/cert_verify_result_android_java.target.linux-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cert_verify_result_android_java.target.linux-x86.mk b/net/cert_verify_result_android_java.target.linux-x86.mk
index 53ce61ed0d..f845bd663c 100644
--- a/net/cert_verify_result_android_java.target.linux-x86.mk
+++ b/net/cert_verify_result_android_java.target.linux-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.darwin-arm.mk b/net/certificate_mime_types_java.target.darwin-arm.mk
index 9fd7fc8eed..2ac0e5a403 100644
--- a/net/certificate_mime_types_java.target.darwin-arm.mk
+++ b/net/certificate_mime_types_java.target.darwin-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.darwin-mips.mk b/net/certificate_mime_types_java.target.darwin-mips.mk
index dccc1ea2ea..2686623ffc 100644
--- a/net/certificate_mime_types_java.target.darwin-mips.mk
+++ b/net/certificate_mime_types_java.target.darwin-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.darwin-x86.mk b/net/certificate_mime_types_java.target.darwin-x86.mk
index d8fb3be860..17ef496271 100644
--- a/net/certificate_mime_types_java.target.darwin-x86.mk
+++ b/net/certificate_mime_types_java.target.darwin-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.linux-arm.mk b/net/certificate_mime_types_java.target.linux-arm.mk
index 9fd7fc8eed..2ac0e5a403 100644
--- a/net/certificate_mime_types_java.target.linux-arm.mk
+++ b/net/certificate_mime_types_java.target.linux-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.linux-mips.mk b/net/certificate_mime_types_java.target.linux-mips.mk
index dccc1ea2ea..2686623ffc 100644
--- a/net/certificate_mime_types_java.target.linux-mips.mk
+++ b/net/certificate_mime_types_java.target.linux-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/certificate_mime_types_java.target.linux-x86.mk b/net/certificate_mime_types_java.target.linux-x86.mk
index d8fb3be860..17ef496271 100644
--- a/net/certificate_mime_types_java.target.linux-x86.mk
+++ b/net/certificate_mime_types_java.target.linux-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index 8187bcb416..2bc0be8480 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -8,7 +8,7 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/test/perftimer.h"
+#include "base/test/perf_time_logger.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_monster_store_test.h"
@@ -97,7 +97,7 @@ class GetCookiesCallback : public BaseCallback {
TEST(ParsedCookieTest, TestParseCookies) {
std::string cookie(kCookieLine);
- PerfTimeLogger timer("Parsed_cookie_parse_cookies");
+ base::PerfTimeLogger timer("Parsed_cookie_parse_cookies");
for (int i = 0; i < kNumCookies; ++i) {
ParsedCookie pc(cookie);
EXPECT_TRUE(pc.IsValid());
@@ -108,7 +108,7 @@ TEST(ParsedCookieTest, TestParseCookies) {
TEST(ParsedCookieTest, TestParseBigCookies) {
std::string cookie(3800, 'z');
cookie += kCookieLine;
- PerfTimeLogger timer("Parsed_cookie_parse_big_cookies");
+ base::PerfTimeLogger timer("Parsed_cookie_parse_big_cookies");
for (int i = 0; i < kNumCookies; ++i) {
ParsedCookie pc(cookie);
EXPECT_TRUE(pc.IsValid());
@@ -126,7 +126,7 @@ TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) {
SetCookieCallback setCookieCallback;
// Add a bunch of cookies on a single host
- PerfTimeLogger timer("Cookie_monster_add_single_host");
+ base::PerfTimeLogger timer("Cookie_monster_add_single_host");
for (std::vector<std::string>::const_iterator it = cookies.begin();
it != cookies.end(); ++it) {
@@ -136,14 +136,14 @@ TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) {
GetCookiesCallback getCookiesCallback;
- PerfTimeLogger timer2("Cookie_monster_query_single_host");
+ base::PerfTimeLogger timer2("Cookie_monster_query_single_host");
for (std::vector<std::string>::const_iterator it = cookies.begin();
it != cookies.end(); ++it) {
getCookiesCallback.GetCookies(cm.get(), GURL(kGoogleURL));
}
timer2.Done();
- PerfTimeLogger timer3("Cookie_monster_deleteall_single_host");
+ base::PerfTimeLogger timer3("Cookie_monster_deleteall_single_host");
cm->DeleteAllAsync(CookieMonster::DeleteCallback());
base::MessageLoop::current()->RunUntilIdle();
timer3.Done();
@@ -160,7 +160,7 @@ TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) {
SetCookieCallback setCookieCallback;
// Add a cookie on a bunch of host
- PerfTimeLogger timer("Cookie_monster_add_many_hosts");
+ base::PerfTimeLogger timer("Cookie_monster_add_many_hosts");
for (std::vector<GURL>::const_iterator it = gurls.begin();
it != gurls.end(); ++it) {
setCookieCallback.SetCookie(cm.get(), *it, cookie);
@@ -169,14 +169,14 @@ TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) {
GetCookiesCallback getCookiesCallback;
- PerfTimeLogger timer2("Cookie_monster_query_many_hosts");
+ base::PerfTimeLogger timer2("Cookie_monster_query_many_hosts");
for (std::vector<GURL>::const_iterator it = gurls.begin();
it != gurls.end(); ++it) {
getCookiesCallback.GetCookies(cm.get(), *it);
}
timer2.Done();
- PerfTimeLogger timer3("Cookie_monster_deleteall_many_hosts");
+ base::PerfTimeLogger timer3("Cookie_monster_deleteall_many_hosts");
cm->DeleteAllAsync(CookieMonster::DeleteCallback());
base::MessageLoop::current()->RunUntilIdle();
timer3.Done();
@@ -229,7 +229,7 @@ TEST_F(CookieMonsterTest, TestDomainTree) {
std::string cookie_line = getCookiesCallback.GetCookies(cm.get(), probe_gurl);
EXPECT_EQ(5, CountInString(cookie_line, '='))
<< "Cookie line: " << cookie_line;
- PerfTimeLogger timer("Cookie_monster_query_domain_tree");
+ base::PerfTimeLogger timer("Cookie_monster_query_domain_tree");
for (int i = 0; i < kNumCookies; i++) {
getCookiesCallback.GetCookies(cm.get(), probe_gurl);
}
@@ -269,7 +269,7 @@ TEST_F(CookieMonsterTest, TestDomainLine) {
cookie_line = getCookiesCallback.GetCookies(cm.get(), probe_gurl);
EXPECT_EQ(32, CountInString(cookie_line, '='));
- PerfTimeLogger timer2("Cookie_monster_query_domain_line");
+ base::PerfTimeLogger timer2("Cookie_monster_query_domain_line");
for (int i = 0; i < kNumCookies; i++) {
getCookiesCallback.GetCookies(cm.get(), probe_gurl);
}
@@ -304,7 +304,7 @@ TEST_F(CookieMonsterTest, TestImport) {
// Import will happen on first access.
GURL gurl("www.google.com");
CookieOptions options;
- PerfTimeLogger timer("Cookie_monster_import_from_store");
+ base::PerfTimeLogger timer("Cookie_monster_import_from_store");
getCookiesCallback.GetCookies(cm.get(), gurl);
timer.Done();
@@ -314,7 +314,7 @@ TEST_F(CookieMonsterTest, TestImport) {
TEST_F(CookieMonsterTest, TestGetKey) {
scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
- PerfTimeLogger timer("Cookie_monster_get_key");
+ base::PerfTimeLogger timer("Cookie_monster_get_key");
for (int i = 0; i < kNumCookies; i++)
cm->GetKey("www.google.com");
timer.Done();
@@ -375,7 +375,7 @@ TEST_F(CookieMonsterTest, TestGCTimes) {
// Trigger the Garbage collection we're allowed.
setCookieCallback.SetCookie(cm.get(), gurl, cookie_line);
- PerfTimeLogger timer((std::string("GC_") + test_case.name).c_str());
+ base::PerfTimeLogger timer((std::string("GC_") + test_case.name).c_str());
for (int i = 0; i < kNumCookies; i++)
setCookieCallback.SetCookie(cm.get(), gurl, cookie_line);
timer.Done();
diff --git a/net/data/url_request_unittest/redirect-test.html.mock-http-headers b/net/data/url_request_unittest/redirect-test.html.mock-http-headers
index 9fdd1c0b7b..c59ef583ab 100644
--- a/net/data/url_request_unittest/redirect-test.html.mock-http-headers
+++ b/net/data/url_request_unittest/redirect-test.html.mock-http-headers
@@ -1,2 +1,3 @@
HTTP/1.1 302 Redirect
Location: with-headers.html
+Cache-Control: max-age=10000
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc
index cb2832f1b1..0f8c3fdd19 100644
--- a/net/disk_cache/backend_impl.cc
+++ b/net/disk_cache/backend_impl.cc
@@ -867,7 +867,7 @@ int32 BackendImpl::GetCurrentEntryId() const {
}
int BackendImpl::MaxFileSize() const {
- return max_size_ / 8;
+ return cache_type() == net::PNACL_CACHE ? max_size_ : max_size_ / 8;
}
void BackendImpl::ModifyStorageSize(int32 old_size, int32 new_size) {
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index 5b7084e7f4..999cc7fffb 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -132,10 +132,8 @@ int DiskCacheBackendTest::GeneratePendingIO(net::TestCompletionCallback* cb) {
return net::ERR_FAILED;
}
- disk_cache::EntryImpl* entry;
- int rv = cache_->CreateEntry("some key",
- reinterpret_cast<disk_cache::Entry**>(&entry),
- cb->callback());
+ disk_cache::Entry* entry;
+ int rv = cache_->CreateEntry("some key", &entry, cb->callback());
if (cb->GetResult(rv) != net::OK)
return net::ERR_CACHE_CREATE_FAILURE;
@@ -147,7 +145,13 @@ int DiskCacheBackendTest::GeneratePendingIO(net::TestCompletionCallback* cb) {
// We are using the current thread as the cache thread because we want to
// be able to call directly this method to make sure that the OS (instead
// of us switching thread) is returning IO pending.
- rv = entry->WriteDataImpl(0, i, buffer.get(), kSize, cb->callback(), false);
+ if (!simple_cache_mode_) {
+ rv = static_cast<disk_cache::EntryImpl*>(entry)->WriteDataImpl(
+ 0, i, buffer.get(), kSize, cb->callback(), false);
+ } else {
+ rv = entry->WriteData(0, i, buffer.get(), kSize, cb->callback(), false);
+ }
+
if (rv == net::ERR_IO_PENDING)
break;
if (rv != kSize)
@@ -156,7 +160,11 @@ int DiskCacheBackendTest::GeneratePendingIO(net::TestCompletionCallback* cb) {
// Don't call Close() to avoid going through the queue or we'll deadlock
// waiting for the operation to finish.
- entry->Release();
+ if (!simple_cache_mode_)
+ static_cast<disk_cache::EntryImpl*>(entry)->Release();
+ else
+ entry->Close();
+
return rv;
}
@@ -502,7 +510,7 @@ void DiskCacheBackendTest::BackendShutdownWithPendingFileIO(bool fast) {
cache_.reset();
if (rv == net::ERR_IO_PENDING) {
- if (fast)
+ if (fast || simple_cache_mode_)
EXPECT_FALSE(cb.have_result());
else
EXPECT_TRUE(cb.have_result());
@@ -3128,9 +3136,22 @@ TEST_F(DiskCacheBackendTest, TracingBackendBasics) {
TracingBackendBasics();
}
-// The simple cache backend isn't intended to work on windows, which has very
-// different file system guarantees from Windows.
-#if !defined(OS_WIN)
+// The Simple Cache backend requires a few guarantees from the filesystem like
+// atomic renaming of recently open files. Those guarantees are not provided in
+// general on Windows.
+#if defined(OS_POSIX)
+
+TEST_F(DiskCacheBackendTest, SimpleCacheShutdownWithPendingCreate) {
+ SetCacheType(net::APP_CACHE);
+ SetSimpleCacheMode();
+ BackendShutdownWithPendingCreate(false);
+}
+
+TEST_F(DiskCacheBackendTest, SimpleCacheShutdownWithPendingFileIO) {
+ SetCacheType(net::APP_CACHE);
+ SetSimpleCacheMode();
+ BackendShutdownWithPendingFileIO(false);
+}
TEST_F(DiskCacheBackendTest, SimpleCacheBasics) {
SetSimpleCacheMode();
@@ -3190,10 +3211,7 @@ TEST_F(DiskCacheBackendTest, SimpleDoomBetween) {
BackendDoomBetween();
}
-// See http://crbug.com/237450.
-// TODO(gavinp): Consider enabling this test again when
-// https://codereview.chromium.org/23823002/ lands.
-TEST_F(DiskCacheBackendTest, DISABLED_SimpleCacheDoomAll) {
+TEST_F(DiskCacheBackendTest, SimpleCacheDoomAll) {
SetSimpleCacheMode();
BackendDoomAll();
}
@@ -3232,7 +3250,7 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) {
// Delete one of the files in the entry.
base::FilePath to_delete_file = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
EXPECT_TRUE(base::PathExists(to_delete_file));
EXPECT_TRUE(disk_cache::DeleteCacheFile(to_delete_file));
@@ -3241,9 +3259,8 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) {
// Confirm the rest of the files are gone.
for (int i = 1; i < disk_cache::kSimpleEntryFileCount; ++i) {
- base::FilePath
- should_be_gone_file(cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i)));
+ base::FilePath should_be_gone_file(cache_path_.AppendASCII(
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i)));
EXPECT_FALSE(base::PathExists(should_be_gone_file));
}
}
@@ -3268,9 +3285,9 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenBadFile) {
entry->Close();
entry = NULL;
- // Write an invalid header on stream 1.
+ // Write an invalid header for stream 0 and stream 1.
base::FilePath entry_file1_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 1));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
disk_cache::SimpleFileHeader header;
header.initial_magic_number = GG_UINT64_C(0xbadf00d);
@@ -3453,4 +3470,8 @@ TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationCorruption) {
EXPECT_TRUE(keys_to_match.empty());
}
-#endif // !defined(OS_WIN)
+// TODO(pasko): Add a Simple Cache test that would simulate upgrade from the
+// version with the index file in the cache directory to the version with the
+// index file in subdirectory.
+
+#endif // defined(OS_POSIX)
diff --git a/net/disk_cache/block_files.cc b/net/disk_cache/block_files.cc
index 9246ef1343..896cdb1632 100644
--- a/net/disk_cache/block_files.cc
+++ b/net/disk_cache/block_files.cc
@@ -52,9 +52,17 @@ BlockHeader::BlockHeader(const BlockHeader& other) : header_(other.header_) {
BlockHeader::~BlockHeader() {
}
-bool BlockHeader::CreateMapBlock(int target, int size, int* index) {
- if (target <= 0 || target > kMaxNumBlocks ||
- size <= 0 || size > kMaxNumBlocks) {
+bool BlockHeader::CreateMapBlock(int size, int* index) {
+ DCHECK(size > 0 && size <= kMaxNumBlocks);
+ int target = 0;
+ for (int i = size; i <= kMaxNumBlocks; i++) {
+ if (header_->empty[i - 1]) {
+ target = i;
+ break;
+ }
+ }
+
+ if (!target) {
NOTREACHED();
return false;
}
@@ -144,10 +152,9 @@ void BlockHeader::DeleteMapBlock(int index, int size) {
// Note that this is a simplified version of DeleteMapBlock().
bool BlockHeader::UsedMapBlock(int index, int size) {
- if (size < 0 || size > kMaxNumBlocks) {
- NOTREACHED();
+ if (size < 0 || size > kMaxNumBlocks)
return false;
- }
+
int byte_index = index / 8;
uint8* byte_map = reinterpret_cast<uint8*>(header_->allocation_map);
uint8 map_block = byte_map[byte_index];
@@ -177,7 +184,7 @@ void BlockHeader::FixAllocationCounters() {
}
}
-bool BlockHeader::NeedToGrowBlockFile(int block_count) {
+bool BlockHeader::NeedToGrowBlockFile(int block_count) const {
bool have_space = false;
int empty_blocks = 0;
for (int i = 0; i < kMaxNumBlocks; i++) {
@@ -195,9 +202,19 @@ bool BlockHeader::NeedToGrowBlockFile(int block_count) {
return !have_space;
}
+bool BlockHeader::CanAllocate(int block_count) const {
+ DCHECK_GT(block_count, 0);
+ for (int i = block_count - 1; i < kMaxNumBlocks; i++) {
+ if (header_->empty[i])
+ return true;
+ }
+
+ return false;
+}
+
int BlockHeader::EmptyBlocks() const {
int empty_blocks = 0;
- for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) {
+ for (int i = 0; i < kMaxNumBlocks; i++) {
empty_blocks += header_->empty[i] * (i + 1);
if (header_->empty[i] < 0)
return 0;
@@ -205,6 +222,14 @@ int BlockHeader::EmptyBlocks() const {
return empty_blocks;
}
+int BlockHeader::MinimumAllocations() const {
+ return header_->empty[kMaxNumBlocks - 1];
+}
+
+int BlockHeader::Capacity() const {
+ return header_->max_entries;
+}
+
bool BlockHeader::ValidateCounters() const {
if (header_->max_entries < 0 || header_->max_entries > kMaxBlocks ||
header_->num_entries < 0)
@@ -217,10 +242,22 @@ bool BlockHeader::ValidateCounters() const {
return true;
}
+int BlockHeader::FileId() const {
+ return header_->this_file;
+}
+
+int BlockHeader::NextFileId() const {
+ return header_->next_file;
+}
+
int BlockHeader::Size() const {
return static_cast<int>(sizeof(*header_));
}
+BlockFileHeader* BlockHeader::Header() {
+ return header_;
+}
+
// ------------------------------------------------------------------------
BlockFiles::BlockFiles(const base::FilePath& path)
@@ -260,7 +297,8 @@ bool BlockFiles::Init(bool create_files) {
MappedFile* BlockFiles::GetFile(Addr address) {
DCHECK(thread_checker_->CalledOnValidThread());
- DCHECK(block_files_.size() >= 4);
+ DCHECK_GE(block_files_.size(),
+ static_cast<size_t>(kFirstAdditionalBlockFile));
DCHECK(address.is_block_file() || !address.is_initialized());
if (!address.is_initialized())
return NULL;
@@ -272,16 +310,20 @@ MappedFile* BlockFiles::GetFile(Addr address) {
if (!OpenBlockFile(file_index))
return NULL;
}
- DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
+ DCHECK_GE(block_files_.size(), static_cast<unsigned int>(file_index));
return block_files_[file_index];
}
bool BlockFiles::CreateBlock(FileType block_type, int block_count,
Addr* block_address) {
DCHECK(thread_checker_->CalledOnValidThread());
- if (block_type < RANKINGS || block_type > BLOCK_4K ||
- block_count < 1 || block_count > 4)
+ DCHECK_NE(block_type, EXTERNAL);
+ DCHECK_NE(block_type, BLOCK_FILES);
+ DCHECK_NE(block_type, BLOCK_ENTRIES);
+ DCHECK_NE(block_type, BLOCK_EVICTED);
+ if (block_count < 1 || block_count > kMaxNumBlocks)
return false;
+
if (!init_)
return false;
@@ -290,22 +332,13 @@ bool BlockFiles::CreateBlock(FileType block_type, int block_count,
return false;
ScopedFlush flush(file);
- BlockHeader header(file);
+ BlockHeader file_header(file);
- int target_size = 0;
- for (int i = block_count; i <= 4; i++) {
- if (header->empty[i - 1]) {
- target_size = i;
- break;
- }
- }
-
- DCHECK(target_size);
int index;
- if (!header.CreateMapBlock(target_size, block_count, &index))
+ if (!file_header.CreateMapBlock(block_count, &index))
return false;
- Addr address(block_type, block_count, header->this_file, index);
+ Addr address(block_type, block_count, file_header.FileId(), index);
block_address->set_value(address.value());
Trace("CreateBlock 0x%x", address.value());
return true;
@@ -332,15 +365,17 @@ void BlockFiles::DeleteBlock(Addr address, bool deep) {
if (deep)
file->Write(zero_buffer_, size, offset);
- BlockHeader header(file);
- header.DeleteMapBlock(address.start_block(), address.num_blocks());
+ BlockHeader file_header(file);
+ file_header.DeleteMapBlock(address.start_block(), address.num_blocks());
file->Flush();
- if (!header->num_entries) {
+ if (!file_header.Header()->num_entries) {
// This file is now empty. Let's try to delete it.
- FileType type = Addr::RequiredFileType(header->entry_size);
- if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size)
+ FileType type = Addr::RequiredFileType(file_header.Header()->entry_size);
+ if (Addr::BlockSizeForFileType(RANKINGS) ==
+ file_header.Header()->entry_size) {
type = RANKINGS;
+ }
RemoveEmptyFile(type); // Ignore failures.
}
}
@@ -450,13 +485,14 @@ bool BlockFiles::OpenBlockFile(int index) {
return false;
}
- BlockHeader header(file.get());
+ BlockHeader file_header(file.get());
+ BlockFileHeader* header = file_header.Header();
if (kBlockMagic != header->magic || kBlockVersion2 != header->version) {
LOG(ERROR) << "Invalid file version or magic " << name.value();
return false;
}
- if (header->updating || !header.ValidateCounters()) {
+ if (header->updating || !file_header.ValidateCounters()) {
// Last instance was not properly shutdown, or counters are out of sync.
if (!FixBlockFileHeader(file.get())) {
LOG(ERROR) << "Unable to fix block file " << name.value();
@@ -516,19 +552,19 @@ bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) {
MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) {
COMPILE_ASSERT(RANKINGS == 1, invalid_file_type);
MappedFile* file = block_files_[block_type - 1];
- BlockHeader header(file);
+ BlockHeader file_header(file);
TimeTicks start = TimeTicks::Now();
- while (header.NeedToGrowBlockFile(block_count)) {
- if (kMaxBlocks == header->max_entries) {
+ while (file_header.NeedToGrowBlockFile(block_count)) {
+ if (kMaxBlocks == file_header.Header()->max_entries) {
file = NextFile(file);
if (!file)
return NULL;
- header = BlockHeader(file);
+ file_header = BlockHeader(file);
continue;
}
- if (!GrowBlockFile(file, header.Get()))
+ if (!GrowBlockFile(file, file_header.Header()))
return NULL;
break;
}
@@ -616,38 +652,39 @@ bool BlockFiles::RemoveEmptyFile(FileType block_type) {
// DCHECK on header->updating because we may be fixing a crash.
bool BlockFiles::FixBlockFileHeader(MappedFile* file) {
ScopedFlush flush(file);
- BlockHeader header(file);
+ BlockHeader file_header(file);
int file_size = static_cast<int>(file->GetLength());
- if (file_size < header.Size())
+ if (file_size < file_header.Size())
return false; // file_size > 2GB is also an error.
const int kMinBlockSize = 36;
const int kMaxBlockSize = 4096;
+ BlockFileHeader* header = file_header.Header();
if (header->entry_size < kMinBlockSize ||
header->entry_size > kMaxBlockSize || header->num_entries < 0)
return false;
// Make sure that we survive crashes.
header->updating = 1;
- int expected = header->entry_size * header->max_entries + header.Size();
+ int expected = header->entry_size * header->max_entries + file_header.Size();
if (file_size != expected) {
- int max_expected = header->entry_size * kMaxBlocks + header.Size();
+ int max_expected = header->entry_size * kMaxBlocks + file_header.Size();
if (file_size < expected || header->empty[3] || file_size > max_expected) {
NOTREACHED();
LOG(ERROR) << "Unexpected file size";
return false;
}
// We were in the middle of growing the file.
- int num_entries = (file_size - header.Size()) / header->entry_size;
+ int num_entries = (file_size - file_header.Size()) / header->entry_size;
header->max_entries = num_entries;
}
- header.FixAllocationCounters();
- int empty_blocks = header.EmptyBlocks();
+ file_header.FixAllocationCounters();
+ int empty_blocks = file_header.EmptyBlocks();
if (empty_blocks + header->num_entries > header->max_entries)
header->num_entries = header->max_entries - empty_blocks;
- if (!header.ValidateCounters())
+ if (!file_header.ValidateCounters())
return false;
header->updating = 0;
@@ -671,7 +708,7 @@ void BlockFiles::GetFileStats(int index, int* used_count, int* load) {
max_blocks += header->max_entries;
int used = header->max_entries;
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < kMaxNumBlocks; i++) {
used -= header->empty[i] * (i + 1);
DCHECK_GE(used, 0);
}
diff --git a/net/disk_cache/block_files.h b/net/disk_cache/block_files.h
index 353c5663df..f8d5483a0b 100644
--- a/net/disk_cache/block_files.h
+++ b/net/disk_cache/block_files.h
@@ -24,7 +24,11 @@ class ThreadChecker;
namespace disk_cache {
// An instance of this class represents the header of a block file in memory.
-// Note that this class doesn't perform any file operation.
+// Note that this class doesn't perform any file operation (as in it only deals
+// with entities in memory).
+// The header of a block file (and hence, this object) is all that is needed to
+// perform common operations like allocating or releasing space for storage;
+// actual access to that storage, however, is not performed through this class.
class NET_EXPORT_PRIVATE BlockHeader {
public:
BlockHeader();
@@ -33,10 +37,9 @@ class NET_EXPORT_PRIVATE BlockHeader {
BlockHeader(const BlockHeader& other);
~BlockHeader();
- // Creates a new entry on the allocation map, updating the apropriate
- // counters. |target| is the type of block to use (number of empty blocks),
- // and |size| is the actual number of blocks to use.
- bool CreateMapBlock(int target, int size, int* index);
+ // Creates a new entry of |size| blocks on the allocation map, updating the
+ // apropriate counters.
+ bool CreateMapBlock(int size, int* index);
// Deletes the block pointed by |index|.
void DeleteMapBlock(int index, int block_size);
@@ -49,20 +52,34 @@ class NET_EXPORT_PRIVATE BlockHeader {
// Returns true if the current block file should not be used as-is to store
// more records. |block_count| is the number of blocks to allocate.
- bool NeedToGrowBlockFile(int block_count);
+ bool NeedToGrowBlockFile(int block_count) const;
+
+ // Returns true if this block file can be used to store an extra record of
+ // size |block_count|.
+ bool CanAllocate(int block_count) const;
// Returns the number of empty blocks for this file.
int EmptyBlocks() const;
+ // Returns the minumum number of allocations that can be satisfied.
+ int MinimumAllocations() const;
+
+ // Returns the number of blocks that this file can store.
+ int Capacity() const;
+
// Returns true if the counters look OK.
bool ValidateCounters() const;
+ // Returns the identifiers of this and the next file (0 if there is none).
+ int FileId() const;
+ int NextFileId() const;
+
// Returns the size of the wrapped structure (BlockFileHeader).
int Size() const;
- BlockFileHeader* operator->() { return header_; }
- void operator=(const BlockHeader& other) { header_ = other.header_; }
- BlockFileHeader* Get() { return header_; }
+ // Returns a pointer to the underlying BlockFileHeader.
+ // TODO(rvargas): This may be removed with the support for V2.
+ BlockFileHeader* Header();
private:
BlockFileHeader* header_;
diff --git a/net/disk_cache/disk_cache_perftest.cc b/net/disk_cache/disk_cache_perftest.cc
index abf39b9b60..6adc4bca70 100644
--- a/net/disk_cache/disk_cache_perftest.cc
+++ b/net/disk_cache/disk_cache_perftest.cc
@@ -9,7 +9,7 @@
#include "base/bind_helpers.h"
#include "base/hash.h"
#include "base/strings/string_util.h"
-#include "base/test/perftimer.h"
+#include "base/test/perf_time_logger.h"
#include "base/test/test_file_util.h"
#include "base/threading/thread.h"
#include "base/timer/timer.h"
@@ -53,7 +53,7 @@ bool TimeWrite(int num_entries, disk_cache::Backend* cache,
MessageLoopHelper helper;
CallbackTest callback(&helper, true);
- PerfTimeLogger timer("Write disk cache entries");
+ base::PerfTimeLogger timer("Write disk cache entries");
for (int i = 0; i < num_entries; i++) {
TestEntry entry;
@@ -107,7 +107,7 @@ bool TimeRead(int num_entries, disk_cache::Backend* cache,
const char* message = cold ? "Read disk cache entries (cold)" :
"Read disk cache entries (warm)";
- PerfTimeLogger timer(message);
+ base::PerfTimeLogger timer(message);
for (int i = 0; i < num_entries; i++) {
disk_cache::Entry* cache_entry;
@@ -150,7 +150,7 @@ TEST_F(DiskCacheTest, Hash) {
int seed = static_cast<int>(Time::Now().ToInternalValue());
srand(seed);
- PerfTimeLogger timer("Hash disk cache keys");
+ base::PerfTimeLogger timer("Hash disk cache keys");
for (int i = 0; i < 300000; i++) {
std::string key = GenerateKey(true);
base::Hash(key);
@@ -223,7 +223,7 @@ TEST_F(DiskCacheTest, BlockFilesPerformance) {
const int kNumEntries = 60000;
disk_cache::Addr* address = new disk_cache::Addr[kNumEntries];
- PerfTimeLogger timer1("Fill three block-files");
+ base::PerfTimeLogger timer1("Fill three block-files");
// Fill up the 32-byte block file (use three files).
for (int i = 0; i < kNumEntries; i++) {
@@ -232,7 +232,7 @@ TEST_F(DiskCacheTest, BlockFilesPerformance) {
}
timer1.Done();
- PerfTimeLogger timer2("Create and delete blocks");
+ base::PerfTimeLogger timer2("Create and delete blocks");
for (int i = 0; i < 200000; i++) {
int entry = rand() * (kNumEntries / RAND_MAX + 1);
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index 34913c773e..3cf98a86b1 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -250,8 +250,9 @@ void DiskCacheTestWithCache::TearDown() {
if (!memory_only_ && !simple_cache_mode_ && integrity_) {
EXPECT_TRUE(CheckCacheIntegrity(cache_path_, new_eviction_, mask_));
}
-
- PlatformTest::TearDown();
+ base::RunLoop().RunUntilIdle();
+ disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
+ DiskCacheTest::TearDown();
}
void DiskCacheTestWithCache::InitMemoryCache() {
diff --git a/net/disk_cache/disk_format_base.h b/net/disk_cache/disk_format_base.h
index c8b7490abf..31983817fc 100644
--- a/net/disk_cache/disk_format_base.h
+++ b/net/disk_cache/disk_format_base.h
@@ -28,6 +28,7 @@ namespace disk_cache {
typedef uint32 CacheAddr;
const uint32 kBlockVersion2 = 0x20000; // Version 2.0.
+const uint32 kBlockCurrentVersion = 0x30000; // Version 3.0.
const uint32 kBlockMagic = 0xC104CAC3;
const int kBlockHeaderSize = 8192; // Two pages: almost 64k entries
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 56371437da..705e6b2d55 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -21,6 +21,7 @@
#include "net/disk_cache/mem_entry_impl.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "net/disk_cache/simple/simple_entry_impl.h"
+#include "net/disk_cache/simple/simple_synchronous_entry.h"
#include "net/disk_cache/simple/simple_test_util.h"
#include "net/disk_cache/simple/simple_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,7 +63,7 @@ class DiskCacheEntryTest : public DiskCacheTestWithCache {
void UpdateSparseEntry();
void DoomSparseEntry();
void PartialSparseEntry();
- bool SimpleCacheMakeBadChecksumEntry(const char* key, int* data_size);
+ bool SimpleCacheMakeBadChecksumEntry(const std::string& key, int* data_size);
};
// This part of the test runs on the background thread.
@@ -2297,8 +2298,9 @@ TEST_F(DiskCacheEntryTest, KeySanityCheck) {
DisableIntegrityCheck();
}
-// The simple cache backend isn't intended to work on Windows, which has very
-// different file system guarantees from Linux.
+// The Simple Cache backend requires a few guarantees from the filesystem like
+// atomic renaming of recently open files. Those guarantees are not provided in
+// general on Windows.
#if defined(OS_POSIX)
TEST_F(DiskCacheEntryTest, SimpleCacheInternalAsyncIO) {
@@ -2413,7 +2415,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomedEntry) {
// Creates an entry with corrupted last byte in stream 0.
// Requires SimpleCacheMode.
-bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key,
+bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const std::string& key,
int* data_size) {
disk_cache::Entry* entry = NULL;
@@ -2427,21 +2429,21 @@ bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key,
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kDataSize));
base::strlcpy(buffer->data(), data, kDataSize);
- EXPECT_EQ(kDataSize, WriteData(entry, 0, 0, buffer.get(), kDataSize, false));
+ EXPECT_EQ(kDataSize, WriteData(entry, 1, 0, buffer.get(), kDataSize, false));
entry->Close();
entry = NULL;
// Corrupt the last byte of the data.
base::FilePath entry_file0_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
int flags = base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_OPEN;
base::PlatformFile entry_file0 =
base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL);
if (entry_file0 == base::kInvalidPlatformFileValue)
return false;
+
int64 file_offset =
- disk_cache::simple_util::GetFileOffsetFromKeyAndDataOffset(
- key, kDataSize - 2);
+ sizeof(disk_cache::SimpleFileHeader) + key.size() + kDataSize - 2;
EXPECT_EQ(1, base::WritePlatformFile(entry_file0, file_offset, "X", 1));
if (!base::ClosePlatformFile(entry_file0))
return false;
@@ -2465,10 +2467,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheBadChecksum) {
ScopedEntryPtr entry_closer(entry);
const int kReadBufferSize = 200;
- EXPECT_GE(kReadBufferSize, entry->GetDataSize(0));
+ EXPECT_GE(kReadBufferSize, entry->GetDataSize(1));
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH,
- ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize));
+ ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize));
}
// Tests that an entry that has had an IO error occur can still be Doomed().
@@ -2487,10 +2489,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheErrorThenDoom) {
ScopedEntryPtr entry_closer(entry);
const int kReadBufferSize = 200;
- EXPECT_GE(kReadBufferSize, entry->GetDataSize(0));
+ EXPECT_GE(kReadBufferSize, entry->GetDataSize(1));
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH,
- ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize));
+ ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize));
entry->Doom(); // Should not crash.
}
@@ -2529,7 +2531,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) {
// record.
int kTruncationBytes = -implicit_cast<int>(sizeof(disk_cache::SimpleFileEOF));
const base::FilePath entry_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
const int64 invalid_size =
disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key,
kTruncationBytes);
@@ -2557,13 +2559,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsBasic) {
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
EXPECT_EQ(
write_buffer->size(),
- WriteData(entry, 0, 0, write_buffer.get(), write_buffer->size(), false));
+ WriteData(entry, 1, 0, write_buffer.get(), write_buffer->size(), false));
scoped_refptr<net::IOBufferWithSize> read_buffer(
new net::IOBufferWithSize(kBufferSize));
- EXPECT_EQ(
- read_buffer->size(),
- ReadData(entry, 0, 0, read_buffer.get(), read_buffer->size()));
+ EXPECT_EQ(read_buffer->size(),
+ ReadData(entry, 1, 0, read_buffer.get(), read_buffer->size()));
}
TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) {
@@ -2590,7 +2591,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) {
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
CallbackTest write_callback(&helper, false);
int ret = entry->WriteData(
- 0,
+ 1,
0,
write_buffer.get(),
write_buffer->size(),
@@ -2623,7 +2624,7 @@ TEST_F(DiskCacheEntryTest,
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
CallbackTest write_callback(&helper, false);
int ret = entry->WriteData(
- 0,
+ 1,
0,
write_buffer.get(),
write_buffer->size(),
@@ -2636,7 +2637,7 @@ TEST_F(DiskCacheEntryTest,
new net::IOBufferWithSize(kBufferSize));
CallbackTest read_callback(&helper, false);
ret = entry->ReadData(
- 0,
+ 1,
0,
read_buffer.get(),
read_buffer->size(),
@@ -2688,7 +2689,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// This write may or may not be optimistic (it depends if the previous
// optimistic create already finished by the time we call the write here).
int ret = entry->WriteData(
- 0,
+ 1,
0,
buffer1.get(),
kSize1,
@@ -2701,7 +2702,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// This Read must not be optimistic, since we don't support that yet.
EXPECT_EQ(net::ERR_IO_PENDING,
entry->ReadData(
- 0,
+ 1,
0,
buffer1_read.get(),
kSize1,
@@ -2714,7 +2715,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// should be empty, so the next Write operation must run as optimistic.
EXPECT_EQ(kSize2,
entry->WriteData(
- 0,
+ 1,
0,
buffer2.get(),
kSize2,
@@ -2725,7 +2726,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// operation finishes and we can then test for HasOneRef() below.
EXPECT_EQ(net::ERR_IO_PENDING,
entry->ReadData(
- 0,
+ 1,
0,
buffer2_read.get(),
kSize2,
@@ -2831,7 +2832,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) {
// operation finishes. Write must fail since we are writing in a closed entry.
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(net::ERR_FAILED, cb.GetResult(net::ERR_IO_PENDING));
// Finish running the pending tasks so that we fully complete the close
@@ -2860,12 +2861,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) {
// entry.
EXPECT_EQ(kSize1,
entry2->WriteData(
- 0, 0, buffer1.get(), kSize1, net::CompletionCallback(), false));
+ 1, 0, buffer1.get(), kSize1, net::CompletionCallback(), false));
// Lets do another read so we block until both the write and the read
// operation finishes and we can then test for HasOneRef() below.
EXPECT_EQ(net::ERR_IO_PENDING,
- entry2->ReadData(0, 0, buffer1.get(), kSize1, cb.callback()));
+ entry2->ReadData(1, 0, buffer1.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
// Check that we are not leaking.
@@ -2896,11 +2897,11 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic5) {
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0, 0, buffer1.get(), kSize1, cb.callback()));
+ entry->ReadData(1, 0, buffer1.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
// Check that we are not leaking.
@@ -2908,9 +2909,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic5) {
static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef());
}
-// TODO(gavinp): Fix this, perhaps by landing
-// https://codereview.chromium.org/23823002/
-TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheOptimistic6) {
+TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) {
// Test sequence:
// Create, Write, Doom, Doom, Read, Doom, Close.
SetSimpleCacheMode();
@@ -2932,7 +2931,7 @@ TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheOptimistic6) {
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
entry->Doom();
@@ -2940,7 +2939,7 @@ TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheOptimistic6) {
// This Read must not be optimistic, since we don't support that yet.
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0, 0, buffer1_read.get(), kSize1, cb.callback()));
+ entry->ReadData(1, 0, buffer1_read.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1));
@@ -2971,7 +2970,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticWriteReleases) {
// operations. To ensure the queue is empty, we issue a write and wait until
// it completes.
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer1.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer1.get(), kWriteSize, false));
EXPECT_TRUE(buffer1->HasOneRef());
// Finally, we should perform an optimistic write and confirm that all
@@ -3016,7 +3015,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheCreateDoomRace) {
for (int i = 0; i < disk_cache::kSimpleEntryFileCount; ++i) {
base::FilePath entry_file_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i));
base::PlatformFileInfo info;
EXPECT_FALSE(file_util::GetFileInfo(entry_file_path, &info));
}
@@ -3053,6 +3052,67 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomCreateRace) {
EXPECT_EQ(net::OK, doom_callback.GetResult(net::ERR_IO_PENDING));
}
+TEST_F(DiskCacheEntryTest, SimpleCacheDoomDoom) {
+ // Test sequence:
+ // Create, Doom, Create, Doom (1st entry), Open.
+ SetSimpleCacheMode();
+ InitCache();
+ disk_cache::Entry* null = NULL;
+
+ const char key[] = "the first key";
+
+ disk_cache::Entry* entry1 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
+ ScopedEntryPtr entry1_closer(entry1);
+ EXPECT_NE(null, entry1);
+
+ EXPECT_EQ(net::OK, DoomEntry(key));
+
+ disk_cache::Entry* entry2 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry2));
+ ScopedEntryPtr entry2_closer(entry2);
+ EXPECT_NE(null, entry2);
+
+ // Redundantly dooming entry1 should not delete entry2.
+ disk_cache::SimpleEntryImpl* simple_entry1 =
+ static_cast<disk_cache::SimpleEntryImpl*>(entry1);
+ net::TestCompletionCallback cb;
+ EXPECT_EQ(net::OK,
+ cb.GetResult(simple_entry1->DoomEntry(cb.callback())));
+
+ disk_cache::Entry* entry3 = NULL;
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry3));
+ ScopedEntryPtr entry3_closer(entry3);
+ EXPECT_NE(null, entry3);
+}
+
+TEST_F(DiskCacheEntryTest, SimpleCacheDoomCreateDoom) {
+ // Test sequence:
+ // Create, Doom, Create, Doom.
+ SetSimpleCacheMode();
+ InitCache();
+
+ disk_cache::Entry* null = NULL;
+
+ const char key[] = "the first key";
+
+ disk_cache::Entry* entry1 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
+ ScopedEntryPtr entry1_closer(entry1);
+ EXPECT_NE(null, entry1);
+
+ entry1->Doom();
+
+ disk_cache::Entry* entry2 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry2));
+ ScopedEntryPtr entry2_closer(entry2);
+ EXPECT_NE(null, entry2);
+
+ entry2->Doom();
+
+ // This test passes if it doesn't crash.
+}
+
// Checks that an optimistic Create would fail later on a racing Open.
TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticCreateFailsOnOpen) {
SetSimpleCacheMode();
@@ -3097,7 +3157,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) {
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kWriteSize));
CacheTestFillBuffer(buffer->data(), kWriteSize, false);
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer.get(), kWriteSize, false));
entry->Close();
AddDelay();
@@ -3106,7 +3166,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) {
ASSERT_EQ(net::OK, CreateEntry(key2 + base::StringPrintf("%d", i), &entry));
ScopedEntryPtr entry_closer(entry);
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer.get(), kWriteSize, false));
}
// TODO(pasko): Find a way to wait for the eviction task(s) to finish by using
@@ -3142,7 +3202,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
EXPECT_EQ(kBufferSize,
- WriteData(entry, 0, 0, write_buffer.get(), kBufferSize, false));
+ WriteData(entry, 1, 0, write_buffer.get(), kBufferSize, false));
entry->Close();
entry = NULL;
@@ -3157,7 +3217,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
CallbackTest read_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0,
+ entry->ReadData(1,
0,
read_buffer.get(),
kReadBufferSize,
@@ -3171,7 +3231,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
CacheTestFillBuffer(truncate_buffer->data(), kReadBufferSize, false);
CallbackTest truncate_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->WriteData(0,
+ entry->WriteData(1,
0,
truncate_buffer.get(),
kReadBufferSize,
@@ -3211,7 +3271,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) {
CallbackTest write_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->WriteData(0,
+ entry->WriteData(1,
0,
write_buffer.get(),
kBufferSize,
@@ -3223,7 +3283,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) {
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kBufferSize));
CallbackTest read_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0,
+ entry->ReadData(1,
0,
read_buffer.get(),
kBufferSize,
@@ -3307,19 +3367,19 @@ TEST_F(DiskCacheEntryTest, SimpleCacheMultipleReadersCheckCRC2) {
disk_cache::Entry* entry = NULL;
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
ScopedEntryPtr entry_closer(entry);
- EXPECT_EQ(1, ReadData(entry, 0, 0, read_buffer1.get(), 1));
+ EXPECT_EQ(1, ReadData(entry, 1, 0, read_buffer1.get(), 1));
// Advance the 2nd reader by the same amount.
disk_cache::Entry* entry2 = NULL;
EXPECT_EQ(net::OK, OpenEntry(key, &entry2));
ScopedEntryPtr entry2_closer(entry2);
- EXPECT_EQ(1, ReadData(entry2, 0, 0, read_buffer2.get(), 1));
+ EXPECT_EQ(1, ReadData(entry2, 1, 0, read_buffer2.get(), 1));
// Continue reading 1st.
- EXPECT_GT(0, ReadData(entry, 0, 1, read_buffer1.get(), size));
+ EXPECT_GT(0, ReadData(entry, 1, 1, read_buffer1.get(), size));
// This read should fail as well because we have previous read failures.
- EXPECT_GT(0, ReadData(entry2, 0, 1, read_buffer2.get(), 1));
+ EXPECT_GT(0, ReadData(entry2, 1, 1, read_buffer2.get(), 1));
DisableIntegrityCheck();
}
@@ -3343,7 +3403,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) {
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
EXPECT_NE(null, entry);
- EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer1.get(), kSize, false));
+ EXPECT_EQ(kSize, WriteData(entry, 1, 0, buffer1.get(), kSize, false));
entry->Close();
disk_cache::Entry* entry2 = NULL;
@@ -3354,14 +3414,14 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) {
int offset = 0;
int buf_len = kHalfSize;
scoped_refptr<net::IOBuffer> buffer1_read1(new net::IOBuffer(buf_len));
- EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read1.get(), buf_len));
+ EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read1.get(), buf_len));
EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read1->data(), buf_len));
// Read the second half of the data.
offset = buf_len;
buf_len = kHalfSize;
scoped_refptr<net::IOBuffer> buffer1_read2(new net::IOBuffer(buf_len));
- EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read2.get(), buf_len));
+ EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read2.get(), buf_len));
char* buffer1_data = buffer1->data() + offset;
EXPECT_EQ(0, memcmp(buffer1_data, buffer1_read2->data(), buf_len));
@@ -3423,4 +3483,78 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonSequentialWrite) {
entry = NULL;
}
+// Test that changing stream1 size does not affect stream0 (stream0 and stream1
+// are stored in the same file in Simple Cache).
+TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
+ SetSimpleCacheMode();
+ InitCache();
+ disk_cache::Entry* entry = NULL;
+ const char key[] = "the key";
+ const int kSize = 100;
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
+ scoped_refptr<net::IOBuffer> buffer_read(new net::IOBuffer(kSize));
+ CacheTestFillBuffer(buffer->data(), kSize, false);
+
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry));
+ EXPECT_TRUE(entry);
+
+ // Write something into stream0.
+ EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer.get(), kSize, false));
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+ entry->Close();
+
+ // Extend stream1.
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ int stream1_size = 100;
+ EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, false));
+ EXPECT_EQ(stream1_size, entry->GetDataSize(1));
+ entry->Close();
+
+ // Check that stream0 data has not been modified and that the EOF record for
+ // stream 0 contains a crc.
+ // The entry needs to be reopened before checking the crc: Open will perform
+ // the synchronization with the previous Close. This ensures the EOF records
+ // have been written to disk before we attempt to read them independently.
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ base::FilePath entry_file0_path = cache_path_.AppendASCII(
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
+ int flags = base::PLATFORM_FILE_READ | base::PLATFORM_FILE_OPEN;
+ base::PlatformFile entry_file0 =
+ base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL);
+ ASSERT_TRUE(entry_file0 != base::kInvalidPlatformFileValue);
+
+ int data_size[disk_cache::kSimpleEntryStreamCount] = {kSize, stream1_size, 0};
+ disk_cache::SimpleEntryStat entry_stat(
+ base::Time::Now(), base::Time::Now(), data_size);
+ int eof_offset = entry_stat.GetEOFOffsetInFile(key, 0);
+ disk_cache::SimpleFileEOF eof_record;
+ ASSERT_EQ(static_cast<int>(sizeof(eof_record)), base::ReadPlatformFile(
+ entry_file0,
+ eof_offset,
+ reinterpret_cast<char*>(&eof_record),
+ sizeof(eof_record)));
+ EXPECT_EQ(disk_cache::kSimpleFinalMagicNumber, eof_record.final_magic_number);
+ EXPECT_TRUE((eof_record.flags & disk_cache::SimpleFileEOF::FLAG_HAS_CRC32) ==
+ disk_cache::SimpleFileEOF::FLAG_HAS_CRC32);
+
+ buffer_read = new net::IOBuffer(kSize);
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+
+ // Shrink stream1.
+ stream1_size = 50;
+ EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, true));
+ EXPECT_EQ(stream1_size, entry->GetDataSize(1));
+ entry->Close();
+
+ // Check that stream0 data has not been modified.
+ buffer_read = new net::IOBuffer(kSize);
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+ entry->Close();
+ entry = NULL;
+}
+
#endif // defined(OS_POSIX)
diff --git a/net/disk_cache/histogram_macros.h b/net/disk_cache/histogram_macros.h
index 3d8011c27f..651bce96f2 100644
--- a/net/disk_cache/histogram_macros.h
+++ b/net/disk_cache/histogram_macros.h
@@ -115,6 +115,9 @@
case net::SHADER_CACHE:\
CACHE_HISTOGRAM_##type(my_name.data(), sample);\
break;\
+ case net::PNACL_CACHE:\
+ CACHE_HISTOGRAM_##type(my_name.data(), sample);\
+ break;\
default:\
NOTREACHED();\
break;\
diff --git a/net/disk_cache/mem_entry_impl.h b/net/disk_cache/mem_entry_impl.h
index ef91f6d7b0..b84cc39ef2 100644
--- a/net/disk_cache/mem_entry_impl.h
+++ b/net/disk_cache/mem_entry_impl.h
@@ -82,11 +82,7 @@ class MemEntryImpl : public Entry {
return parent_ ? kChildEntry : kParentEntry;
}
- std::string& key() {
- return key_;
- }
-
- net::BoundNetLog& net_log() {
+ const net::BoundNetLog& net_log() {
return net_log_;
}
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index 983b211b49..9e926bb7e4 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -33,6 +33,7 @@
#include "net/disk_cache/simple/simple_index_file.h"
#include "net/disk_cache/simple/simple_synchronous_entry.h"
#include "net/disk_cache/simple/simple_util.h"
+#include "net/disk_cache/simple/simple_version_upgrade.h"
using base::Callback;
using base::Closure;
@@ -132,65 +133,12 @@ void DeleteBackendImpl(disk_cache::Backend** backend,
// Detects if the files in the cache directory match the current disk cache
// backend type and version. If the directory contains no cache, occupies it
// with the fresh structure.
-//
-// There is a convention among disk cache backends: looking at the magic in the
-// file "index" it should be sufficient to determine if the cache belongs to the
-// currently running backend. The Simple Backend stores its index in the file
-// "the-real-index" (see simple_index.cc) and the file "index" only signifies
-// presence of the implementation's magic and version. There are two reasons for
-// that:
-// 1. Absence of the index is itself not a fatal error in the Simple Backend
-// 2. The Simple Backend has pickled file format for the index making it hacky
-// to have the magic in the right place.
bool FileStructureConsistent(const base::FilePath& path) {
if (!base::PathExists(path) && !file_util::CreateDirectory(path)) {
LOG(ERROR) << "Failed to create directory: " << path.LossyDisplayName();
return false;
}
- const base::FilePath fake_index = path.AppendASCII("index");
- base::PlatformFileError error;
- base::PlatformFile fake_index_file = base::CreatePlatformFile(
- fake_index,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
- NULL,
- &error);
- if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
- base::PlatformFile file = base::CreatePlatformFile(
- fake_index,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
- NULL, &error);
- disk_cache::SimpleFileHeader file_contents;
- file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
- file_contents.version = disk_cache::kSimpleVersion;
- int bytes_written = base::WritePlatformFile(
- file, 0, reinterpret_cast<char*>(&file_contents),
- sizeof(file_contents));
- if (!base::ClosePlatformFile(file) ||
- bytes_written != sizeof(file_contents)) {
- LOG(ERROR) << "Failed to write cache structure file: "
- << path.LossyDisplayName();
- return false;
- }
- return true;
- } else if (error != base::PLATFORM_FILE_OK) {
- LOG(ERROR) << "Could not open cache structure file: "
- << path.LossyDisplayName();
- return false;
- } else {
- disk_cache::SimpleFileHeader file_header;
- int bytes_read = base::ReadPlatformFile(
- fake_index_file, 0, reinterpret_cast<char*>(&file_header),
- sizeof(file_header));
- if (!base::ClosePlatformFile(fake_index_file) ||
- bytes_read != sizeof(file_header) ||
- file_header.initial_magic_number !=
- disk_cache::kSimpleInitialMagicNumber ||
- file_header.version != disk_cache::kSimpleVersion) {
- LOG(ERROR) << "File structure does not match the disk cache backend.";
- return false;
- }
- return true;
- }
+ return disk_cache::UpgradeSimpleCacheOnDisk(path);
}
// A short bindable thunk that can call a completion callback. Intended to be
diff --git a/net/disk_cache/simple/simple_backend_version.h b/net/disk_cache/simple/simple_backend_version.h
new file mode 100644
index 0000000000..fe350a2e88
--- /dev/null
+++ b/net/disk_cache/simple/simple_backend_version.h
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_VERSION_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_VERSION_H_
+
+namespace disk_cache {
+
+// Short rules helping to think about data upgrades within Simple Cache:
+// * ALL changes of on-disk data format, backward-compatible or not,
+// forward-compatible or not, require updating the |kSimpleVersion|.
+// * All cache Upgrades are performed on backend start, must be finished
+// before the new backend starts processing any incoming operations.
+// * If the Upgrade is not implemented for transition from
+// |kSimpleVersion - 1| then the whole cache directory will be cleared.
+// * Dropping cache data on disk or some of its parts can be a valid way to
+// Upgrade.
+const uint32 kSimpleVersion = 6;
+
+// The version of the entry file(s) as written to disk. Must be updated iff the
+// entry format changes with the overall backend version update.
+const uint32 kSimpleEntryVersionOnDisk = 5;
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_VERSION_H_
diff --git a/net/disk_cache/simple/simple_entry_format.h b/net/disk_cache/simple/simple_entry_format.h
index d06ab1139c..8224b858dc 100644
--- a/net/disk_cache/simple/simple_entry_format.h
+++ b/net/disk_cache/simple/simple_entry_format.h
@@ -19,17 +19,21 @@ namespace disk_cache {
const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30);
const uint64 kSimpleFinalMagicNumber = GG_UINT64_C(0xf4fa6f45970d41d8);
-// A file in the Simple cache consists of a SimpleFileHeader followed
-// by data.
+// A file containing stream 0 and stream 1 in the Simple cache consists of:
+// - a SimpleFileHeader.
+// - the key.
+// - the data from stream 1.
+// - a SimpleFileEOF record for stream 1.
+// - the data from stream 0.
+// - a SimpleFileEOF record for stream 0.
-// A file in the Simple cache consists of:
+// A file containing stream 2 in the Simple cache consists of:
// - a SimpleFileHeader.
// - the key.
// - the data.
// - at the end, a SimpleFileEOF record.
-const uint32 kSimpleVersion = 4;
-
-static const int kSimpleEntryFileCount = 3;
+static const int kSimpleEntryFileCount = 2;
+static const int kSimpleEntryStreamCount = 3;
struct NET_EXPORT_PRIVATE SimpleFileHeader {
SimpleFileHeader();
@@ -40,7 +44,7 @@ struct NET_EXPORT_PRIVATE SimpleFileHeader {
uint32 key_hash;
};
-struct SimpleFileEOF {
+struct NET_EXPORT_PRIVATE SimpleFileEOF {
enum Flags {
FLAG_HAS_CRC32 = (1U << 0),
};
@@ -50,6 +54,8 @@ struct SimpleFileEOF {
uint64 final_magic_number;
uint32 flags;
uint32 data_crc32;
+ // |stream_size| is only used in the EOF record for stream 0.
+ uint32 stream_size;
};
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_format_history.h b/net/disk_cache/simple/simple_entry_format_history.h
new file mode 100644
index 0000000000..f7b818a59e
--- /dev/null
+++ b/net/disk_cache/simple/simple_entry_format_history.h
@@ -0,0 +1,62 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_FORMAT_HISTORY_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_FORMAT_HISTORY_H_
+
+#include "base/basictypes.h"
+#include "base/port.h"
+#include "net/base/net_export.h"
+
+namespace disk_cache {
+
+namespace simplecache_v5 {
+
+const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30);
+const uint64 kSimpleFinalMagicNumber = GG_UINT64_C(0xf4fa6f45970d41d8);
+
+// A file containing stream 0 and stream 1 in the Simple cache consists of:
+// - a SimpleFileHeader.
+// - the key.
+// - the data from stream 1.
+// - a SimpleFileEOF record for stream 1.
+// - the data from stream 0.
+// - a SimpleFileEOF record for stream 0.
+
+// A file containing stream 2 in the Simple cache consists of:
+// - a SimpleFileHeader.
+// - the key.
+// - the data.
+// - at the end, a SimpleFileEOF record.
+static const int kSimpleEntryFileCount = 2;
+static const int kSimpleEntryStreamCount = 3;
+
+struct NET_EXPORT_PRIVATE SimpleFileHeader {
+ SimpleFileHeader();
+
+ uint64 initial_magic_number;
+ uint32 version;
+ uint32 key_length;
+ uint32 key_hash;
+};
+
+struct NET_EXPORT_PRIVATE SimpleFileEOF {
+ enum Flags {
+ FLAG_HAS_CRC32 = (1U << 0),
+ };
+
+ SimpleFileEOF();
+
+ uint64 final_magic_number;
+ uint32 flags;
+ uint32 data_crc32;
+ // |stream_size| is only used in the EOF record for stream 0.
+ uint32 stream_size;
+};
+
+} // namespace simplecache_v5
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_FORMAT_HISTORY_H_
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index e4e7220af7..5f24564f4c 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -28,6 +28,7 @@
#include "net/disk_cache/simple/simple_util.h"
#include "third_party/zlib/zlib.h"
+namespace disk_cache {
namespace {
// Used in histograms, please only add entries at the end.
@@ -121,9 +122,17 @@ void AdjustOpenEntryCountBy(net::CacheType cache_type, int offset) {
"GlobalOpenEntryCount", cache_type, g_open_entry_count);
}
-} // namespace
+void InvokeCallbackIfBackendIsAlive(
+ const base::WeakPtr<SimpleBackendImpl>& backend,
+ const net::CompletionCallback& completion_callback,
+ int result) {
+ DCHECK(!completion_callback.is_null());
+ if (!backend.get())
+ return;
+ completion_callback.Run(result);
+}
-namespace disk_cache {
+} // namespace
using base::Closure;
using base::FilePath;
@@ -165,7 +174,8 @@ SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type,
state_(STATE_UNINITIALIZED),
synchronous_entry_(NULL),
net_log_(net::BoundNetLog::Make(
- net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)) {
+ net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)),
+ stream_0_data_(new net::GrowableIOBuffer()) {
COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_end_offset_),
arrays_should_be_same_size);
COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_),
@@ -253,10 +263,14 @@ int SimpleEntryImpl::CreateEntry(Entry** out_entry,
}
int SimpleEntryImpl::DoomEntry(const CompletionCallback& callback) {
+ if (doomed_)
+ return net::OK;
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_DOOM_CALL);
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_DOOM_BEGIN);
MarkAsDoomed();
+ if (backend_.get())
+ backend_->OnDoomStart(entry_hash_);
pending_operations_.push(SimpleEntryOperation::DoomOperation(this, callback));
RunNextOperationIfNeeded();
return net::ERR_IO_PENDING;
@@ -324,7 +338,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
false));
}
- if (stream_index < 0 || stream_index >= kSimpleEntryFileCount ||
+ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
buf_len < 0) {
if (net_log_.IsLoggingAllEvents()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
@@ -345,6 +359,8 @@ int SimpleEntryImpl::ReadData(int stream_index,
return 0;
}
+ // TODO(clamy): return immediatly when reading from stream 0.
+
// TODO(felipeg): Optimization: Add support for truly parallel read
// operations.
bool alone_in_queue =
@@ -370,8 +386,8 @@ int SimpleEntryImpl::WriteData(int stream_index,
truncate));
}
- if (stream_index < 0 || stream_index >= kSimpleEntryFileCount || offset < 0 ||
- buf_len < 0) {
+ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
+ offset < 0 || buf_len < 0) {
if (net_log_.IsLoggingAllEvents()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
@@ -391,16 +407,11 @@ int SimpleEntryImpl::WriteData(int stream_index,
}
ScopedOperationRunner operation_runner(this);
- // Currently, Simple Cache is only used for HTTP, which stores the headers in
- // stream 0 and always writes them with a single, truncating write. Detect
- // these writes and record the size and size changes of the headers. Also,
- // note writes to stream 0 that violate those assumptions.
- if (stream_index == 0) {
- if (offset == 0 && truncate)
- RecordHeaderSizeChange(cache_type_, data_size_[0], buf_len);
- else
- RecordUnexpectedStream0Write(cache_type_);
- }
+ // Stream 0 data is kept in memory, so can be written immediatly if there are
+ // no IO operations pending.
+ if (stream_index == 0 && state_ == STATE_READY &&
+ pending_operations_.size() == 0)
+ return SetStream0Data(buf, offset, buf_len, truncate);
// We can only do optimistic Write if there is no pending operations, so
// that we are sure that the next call to RunNextOperationIfNeeded will
@@ -504,6 +515,17 @@ SimpleEntryImpl::~SimpleEntryImpl() {
net_log_.EndEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY);
}
+void SimpleEntryImpl::PostClientCallback(const CompletionCallback& callback,
+ int result) {
+ if (callback.is_null())
+ return;
+ // Note that the callback is posted rather than directly invoked to avoid
+ // reentrancy issues.
+ MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&InvokeCallbackIfBackendIsAlive, backend_, callback, result));
+}
+
void SimpleEntryImpl::MakeUninitialized() {
state_ = STATE_UNINITIALIZED;
std::memset(crc32s_end_offset_, 0, sizeof(crc32s_end_offset_));
@@ -519,6 +541,15 @@ void SimpleEntryImpl::ReturnEntryToCaller(Entry** out_entry) {
DCHECK(out_entry);
++open_count_;
AddRef(); // Balanced in Close()
+ if (!backend_.get()) {
+ // This method can be called when an asynchronous operation completed.
+ // If the backend no longer exists, the callback won't be invoked, and so we
+ // must close ourselves to avoid leaking. As well, there's no guarantee the
+ // client-provided pointer (|out_entry|) hasn't been freed, and no point
+ // dereferencing it, either.
+ Close();
+ return;
+ }
*out_entry = this;
}
@@ -529,9 +560,9 @@ void SimpleEntryImpl::RemoveSelfFromBackend() {
}
void SimpleEntryImpl::MarkAsDoomed() {
+ doomed_ = true;
if (!backend_.get())
return;
- doomed_ = true;
backend_->index()->Remove(entry_hash_);
RemoveSelfFromBackend();
}
@@ -599,18 +630,14 @@ void SimpleEntryImpl::OpenEntryInternal(bool have_index,
if (state_ == STATE_READY) {
ReturnEntryToCaller(out_entry);
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(callback,
- net::OK));
+ PostClientCallback(callback, net::OK);
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END,
CreateNetLogSimpleEntryCreationCallback(this, net::OK));
return;
}
if (state_ == STATE_FAILURE) {
- if (!callback.is_null()) {
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- callback, net::ERR_FAILED));
- }
+ PostClientCallback(callback, net::ERR_FAILED);
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END,
CreateNetLogSimpleEntryCreationCallback(this, net::ERR_FAILED));
@@ -652,11 +679,7 @@ void SimpleEntryImpl::CreateEntryInternal(bool have_index,
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_CREATE_END,
CreateNetLogSimpleEntryCreationCallback(this, net::ERR_FAILED));
-
- if (!callback.is_null()) {
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- callback, net::ERR_FAILED));
- }
+ PostClientCallback(callback, net::ERR_FAILED);
return;
}
DCHECK_EQ(STATE_UNINITIALIZED, state_);
@@ -669,7 +692,7 @@ void SimpleEntryImpl::CreateEntryInternal(bool have_index,
last_used_ = last_modified_ = base::Time::Now();
// If creation succeeds, we should mark all streams to be saved on close.
- for (int i = 0; i < kSimpleEntryFileCount; ++i)
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i)
have_written_[i] = true;
const base::TimeTicks start_time = base::TimeTicks::Now();
@@ -704,7 +727,7 @@ void SimpleEntryImpl::CloseInternal() {
if (state_ == STATE_READY) {
DCHECK(synchronous_entry_);
state_ = STATE_IO_PENDING;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
if (have_written_[i]) {
if (GetDataSize(i) == crc32s_end_offset_[i]) {
int32 crc = GetDataSize(i) == 0 ? crc32(0, Z_NULL, 0) : crc32s_[i];
@@ -723,12 +746,13 @@ void SimpleEntryImpl::CloseInternal() {
base::Bind(&SimpleSynchronousEntry::Close,
base::Unretained(synchronous_entry_),
SimpleEntryStat(last_used_, last_modified_, data_size_),
- base::Passed(&crc32s_to_write));
+ base::Passed(&crc32s_to_write),
+ stream_0_data_);
Closure reply = base::Bind(&SimpleEntryImpl::CloseOperationComplete, this);
synchronous_entry_ = NULL;
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
if (!have_written_[i]) {
SIMPLE_CACHE_UMA(ENUMERATION,
"CheckCRCResult", cache_type_,
@@ -758,8 +782,11 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) {
if (!callback.is_null()) {
RecordReadResult(cache_type_, READ_RESULT_BAD_STATE);
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- callback, net::ERR_FAILED));
+ // Note that the API states that client-provided callbacks for entry-level
+ // (i.e. non-backend) operations (e.g. read, write) are invoked even if
+ // the backend was already destroyed.
+ MessageLoopProxy::current()->PostTask(
+ FROM_HERE, base::Bind(callback, net::ERR_FAILED));
}
if (net_log_.IsLoggingAllEvents()) {
net_log_.AddEvent(
@@ -774,27 +801,37 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
// If there is nothing to read, we bail out before setting state_ to
// STATE_IO_PENDING.
if (!callback.is_null())
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- callback, 0));
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(callback, 0));
return;
}
buf_len = std::min(buf_len, GetDataSize(stream_index) - offset);
+ // Since stream 0 data is kept in memory, it is read immediately.
+ if (stream_index == 0) {
+ int ret_value = ReadStream0Data(buf, offset, buf_len);
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE,
+ base::Bind(callback, ret_value));
+ }
+ return;
+ }
+
state_ = STATE_IO_PENDING;
if (!doomed_ && backend_.get())
backend_->index()->UseIfExists(entry_hash_);
scoped_ptr<uint32> read_crc32(new uint32());
scoped_ptr<int> result(new int());
- scoped_ptr<base::Time> last_used(new base::Time());
+ scoped_ptr<SimpleEntryStat> entry_stat(
+ new SimpleEntryStat(last_used_, last_modified_, data_size_));
Closure task = base::Bind(
&SimpleSynchronousEntry::ReadData,
base::Unretained(synchronous_entry_),
SimpleSynchronousEntry::EntryOperationData(stream_index, offset, buf_len),
make_scoped_refptr(buf),
read_crc32.get(),
- last_used.get(),
+ entry_stat.get(),
result.get());
Closure reply = base::Bind(&SimpleEntryImpl::ReadOperationComplete,
this,
@@ -802,7 +839,7 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
offset,
callback,
base::Passed(&read_crc32),
- base::Passed(&last_used),
+ base::Passed(&entry_stat),
base::Passed(&result));
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
}
@@ -831,34 +868,30 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED));
}
if (!callback.is_null()) {
- // We need to posttask so that we don't go in a loop when we call the
- // callback directly.
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- callback, net::ERR_FAILED));
+ MessageLoopProxy::current()->PostTask(
+ FROM_HERE, base::Bind(callback, net::ERR_FAILED));
}
// |this| may be destroyed after return here.
return;
}
DCHECK_EQ(STATE_READY, state_);
+
+ // Since stream 0 data is kept in memory, it will be written immediatly.
+ if (stream_index == 0) {
+ int ret_value = SetStream0Data(buf, offset, buf_len, truncate);
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE,
+ base::Bind(callback, ret_value));
+ }
+ return;
+ }
+
state_ = STATE_IO_PENDING;
if (!doomed_ && backend_.get())
backend_->index()->UseIfExists(entry_hash_);
- // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|)
- // if |offset == 0| or we have already computed the CRC for [0 .. offset).
- // We rely on most write operations being sequential, start to end to compute
- // the crc of the data. When we write to an entry and close without having
- // done a sequential write, we don't check the CRC on read.
- if (offset == 0 || crc32s_end_offset_[stream_index] == offset) {
- uint32 initial_crc = (offset != 0) ? crc32s_[stream_index]
- : crc32(0, Z_NULL, 0);
- if (buf_len > 0) {
- crc32s_[stream_index] = crc32(initial_crc,
- reinterpret_cast<const Bytef*>(buf->data()),
- buf_len);
- }
- crc32s_end_offset_[stream_index] = offset + buf_len;
- }
+
+ AdvanceCrc(buf, offset, buf_len, stream_index);
// |entry_stat| needs to be initialized before modifying |data_size_|.
scoped_ptr<SimpleEntryStat> entry_stat(
@@ -875,6 +908,10 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
last_used_ = last_modified_ = base::Time::Now();
have_written_[stream_index] = true;
+ // Writing on stream 1 affects the placement of stream 0 in the file, the EOF
+ // record will have to be rewritten.
+ if (stream_index == 1)
+ have_written_[0] = true;
scoped_ptr<int> result(new int());
Closure task = base::Bind(&SimpleSynchronousEntry::WriteData,
@@ -894,8 +931,6 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
}
void SimpleEntryImpl::DoomEntryInternal(const CompletionCallback& callback) {
- if (backend_)
- backend_->OnDoomStart(entry_hash_);
PostTaskAndReplyWithResult(
worker_pool_, FROM_HERE,
base::Bind(&SimpleSynchronousEntry::DoomEntry, path_, key_, entry_hash_),
@@ -922,11 +957,7 @@ void SimpleEntryImpl::CreationOperationComplete(
MarkAsDoomed();
net_log_.AddEventWithNetErrorCode(end_event_type, net::ERR_FAILED);
-
- if (!completion_callback.is_null()) {
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- completion_callback, net::ERR_FAILED));
- }
+ PostClientCallback(completion_callback, net::ERR_FAILED);
MakeUninitialized();
return;
}
@@ -937,6 +968,13 @@ void SimpleEntryImpl::CreationOperationComplete(
state_ = STATE_READY;
synchronous_entry_ = in_results->sync_entry;
+ if (in_results->stream_0_data) {
+ stream_0_data_ = in_results->stream_0_data;
+ // The crc was read in SimpleSynchronousEntry.
+ crc_check_state_[0] = CRC_CHECK_DONE;
+ crc32s_[0] = in_results->stream_0_crc32;
+ crc32s_end_offset_[0] = in_results->entry_stat.data_size(0);
+ }
if (key_.empty()) {
SetKey(synchronous_entry_->key());
} else {
@@ -951,10 +989,7 @@ void SimpleEntryImpl::CreationOperationComplete(
AdjustOpenEntryCountBy(cache_type_, 1);
net_log_.AddEvent(end_event_type);
- if (!completion_callback.is_null()) {
- MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
- completion_callback, net::OK));
- }
+ PostClientCallback(completion_callback, net::OK);
}
void SimpleEntryImpl::EntryOperationComplete(
@@ -987,7 +1022,7 @@ void SimpleEntryImpl::ReadOperationComplete(
int offset,
const CompletionCallback& completion_callback,
scoped_ptr<uint32> read_crc32,
- scoped_ptr<base::Time> last_used,
+ scoped_ptr<SimpleEntryStat> entry_stat,
scoped_ptr<int> result) {
DCHECK(io_thread_checker_.CalledOnValidThread());
DCHECK(synchronous_entry_);
@@ -1023,7 +1058,7 @@ void SimpleEntryImpl::ReadOperationComplete(
Closure task = base::Bind(&SimpleSynchronousEntry::CheckEOFRecord,
base::Unretained(synchronous_entry_),
stream_index,
- data_size_[stream_index],
+ *entry_stat,
crc32s_[stream_index],
new_result.get());
Closure reply = base::Bind(&SimpleEntryImpl::ChecksumOperationComplete,
@@ -1052,10 +1087,7 @@ void SimpleEntryImpl::ReadOperationComplete(
}
EntryOperationComplete(
- stream_index,
- completion_callback,
- SimpleEntryStat(*last_used, last_modified_, data_size_),
- result.Pass());
+ stream_index, completion_callback, *entry_stat, result.Pass());
}
void SimpleEntryImpl::WriteOperationComplete(
@@ -1142,10 +1174,10 @@ void SimpleEntryImpl::UpdateDataFromEntryStat(
DCHECK(synchronous_entry_);
DCHECK_EQ(STATE_READY, state_);
- last_used_ = entry_stat.last_used;
- last_modified_ = entry_stat.last_modified;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- data_size_[i] = entry_stat.data_size[i];
+ last_used_ = entry_stat.last_used();
+ last_modified_ = entry_stat.last_modified();
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
+ data_size_[i] = entry_stat.data_size(i);
}
if (!doomed_ && backend_.get())
backend_->index()->UpdateEntrySize(entry_hash_, GetDiskUsage());
@@ -1153,7 +1185,7 @@ void SimpleEntryImpl::UpdateDataFromEntryStat(
int64 SimpleEntryImpl::GetDiskUsage() const {
int64 file_size = 0;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
file_size +=
simple_util::GetFileSizeFromKeyAndDataSize(key_, data_size_[i]);
}
@@ -1231,4 +1263,76 @@ void SimpleEntryImpl::RecordWriteDependencyType(
type, WRITE_DEPENDENCY_TYPE_MAX);
}
+int SimpleEntryImpl::ReadStream0Data(net::IOBuffer* buf,
+ int offset,
+ int buf_len) {
+ if (buf_len < 0) {
+ RecordReadResult(cache_type_, READ_RESULT_SYNC_READ_FAILURE);
+ return 0;
+ }
+ memcpy(buf->data(), stream_0_data_->data() + offset, buf_len);
+ UpdateDataFromEntryStat(
+ SimpleEntryStat(base::Time::Now(), last_modified_, data_size_));
+ RecordReadResult(cache_type_, READ_RESULT_SUCCESS);
+ return buf_len;
+}
+
+int SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf,
+ int offset,
+ int buf_len,
+ bool truncate) {
+ // Currently, stream 0 is only used for HTTP headers, and always writes them
+ // with a single, truncating write. Detect these writes and record the size
+ // changes of the headers. Also, support writes to stream 0 that have
+ // different access patterns, as required by the API contract.
+ // All other clients of the Simple Cache are encouraged to use stream 1.
+ have_written_[0] = true;
+ int data_size = GetDataSize(0);
+ if (offset == 0 && truncate) {
+ RecordHeaderSizeChange(cache_type_, data_size, buf_len);
+ stream_0_data_->SetCapacity(buf_len);
+ memcpy(stream_0_data_->data(), buf->data(), buf_len);
+ data_size_[0] = buf_len;
+ } else {
+ RecordUnexpectedStream0Write(cache_type_);
+ const int buffer_size =
+ truncate ? offset + buf_len : std::max(offset + buf_len, data_size);
+ stream_0_data_->SetCapacity(buffer_size);
+ // If |stream_0_data_| was extended, the extension until offset needs to be
+ // zero-filled.
+ const int fill_size = offset <= data_size ? 0 : offset - data_size;
+ if (fill_size > 0)
+ memset(stream_0_data_->data() + data_size, 0, fill_size);
+ if (buf)
+ memcpy(stream_0_data_->data() + offset, buf->data(), buf_len);
+ data_size_[0] = buffer_size;
+ }
+ base::Time modification_time = base::Time::Now();
+ AdvanceCrc(buf, offset, buf_len, 0);
+ UpdateDataFromEntryStat(
+ SimpleEntryStat(modification_time, modification_time, data_size_));
+ RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
+ return buf_len;
+}
+
+void SimpleEntryImpl::AdvanceCrc(net::IOBuffer* buffer,
+ int offset,
+ int length,
+ int stream_index) {
+ // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|)
+ // if |offset == 0| or we have already computed the CRC for [0 .. offset).
+ // We rely on most write operations being sequential, start to end to compute
+ // the crc of the data. When we write to an entry and close without having
+ // done a sequential write, we don't check the CRC on read.
+ if (offset == 0 || crc32s_end_offset_[stream_index] == offset) {
+ uint32 initial_crc =
+ (offset != 0) ? crc32s_[stream_index] : crc32(0, Z_NULL, 0);
+ if (length > 0) {
+ crc32s_[stream_index] = crc32(
+ initial_crc, reinterpret_cast<const Bytef*>(buffer->data()), length);
+ }
+ crc32s_end_offset_[stream_index] = offset + length;
+ }
+}
+
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index 4f07a3e18f..e2f0c63b39 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -14,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "net/base/cache_type.h"
+#include "net/base/net_export.h"
#include "net/base/net_log.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/simple/simple_entry_format.h"
@@ -24,6 +25,7 @@ class TaskRunner;
}
namespace net {
+class GrowableIOBuffer;
class IOBuffer;
}
@@ -31,13 +33,14 @@ namespace disk_cache {
class SimpleBackendImpl;
class SimpleSynchronousEntry;
-struct SimpleEntryStat;
+class SimpleEntryStat;
struct SimpleEntryCreationResults;
// SimpleEntryImpl is the IO thread interface to an entry in the very simple
// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO
// on the worker thread.
-class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
+class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
+ public base::RefCounted<SimpleEntryImpl>,
public base::SupportsWeakPtr<SimpleEntryImpl> {
friend class base::RefCounted<SimpleEntryImpl>;
public:
@@ -134,6 +137,12 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
virtual ~SimpleEntryImpl();
+ // Must be used to invoke a client-provided completion callback for an
+ // operation initiated through the backend (e.g. create, open) so that clients
+ // don't get notified after they deleted the backend (which they would not
+ // expect).
+ void PostClientCallback(const CompletionCallback& callback, int result);
+
// Sets entry to STATE_UNINITIALIZED.
void MakeUninitialized();
@@ -209,7 +218,7 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
int offset,
const CompletionCallback& completion_callback,
scoped_ptr<uint32> read_crc32,
- scoped_ptr<base::Time> last_used,
+ scoped_ptr<SimpleEntryStat> entry_stat,
scoped_ptr<int> result);
// Called after an asynchronous write completes.
@@ -243,6 +252,23 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
void RecordReadIsParallelizable(const SimpleEntryOperation& operation) const;
void RecordWriteDependencyType(const SimpleEntryOperation& operation) const;
+ // Reads from the stream 0 data kept in memory.
+ int ReadStream0Data(net::IOBuffer* buf, int offset, int buf_len);
+
+ // Copies data from |buf| to the internal in-memory buffer for stream 0. If
+ // |truncate| is set to true, the target buffer will be truncated at |offset|
+ // + |buf_len| before being written.
+ int SetStream0Data(net::IOBuffer* buf,
+ int offset, int buf_len,
+ bool truncate);
+
+ // Updates |crc32s_| and |crc32s_end_offset_| for a write of the data in
+ // |buffer| on |stream_index|, starting at |offset| and of length |length|.
+ void AdvanceCrc(net::IOBuffer* buffer,
+ int offset,
+ int length,
+ int stream_index);
+
// All nonstatic SimpleEntryImpl methods should always be called on the IO
// thread, in all cases. |io_thread_checker_| documents and enforces this.
base::ThreadChecker io_thread_checker_;
@@ -260,7 +286,7 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
// TODO(clamy): Unify last_used_ with data in the index.
base::Time last_used_;
base::Time last_modified_;
- int32 data_size_[kSimpleEntryFileCount];
+ int32 data_size_[kSimpleEntryStreamCount];
// Number of times this object has been returned from Backend::OpenEntry() and
// Backend::CreateEntry() without subsequent Entry::Close() calls. Used to
@@ -275,15 +301,16 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
// write. For each stream, |crc32s_[index]| is the crc32 of that stream from
// [0 .. |crc32s_end_offset_|). If |crc32s_end_offset_[index] == 0| then the
// value of |crc32s_[index]| is undefined.
- int32 crc32s_end_offset_[kSimpleEntryFileCount];
- uint32 crc32s_[kSimpleEntryFileCount];
+ int32 crc32s_end_offset_[kSimpleEntryStreamCount];
+ uint32 crc32s_[kSimpleEntryStreamCount];
- // If |have_written_[index]| is true, we have written to the stream |index|.
- bool have_written_[kSimpleEntryFileCount];
+ // If |have_written_[index]| is true, we have written to the file that
+ // contains stream |index|.
+ bool have_written_[kSimpleEntryStreamCount];
// Reflects how much CRC checking has been done with the entry. This state is
// reported on closing each entry stream.
- CheckCrcResult crc_check_state_[kSimpleEntryFileCount];
+ CheckCrcResult crc_check_state_[kSimpleEntryStreamCount];
// The |synchronous_entry_| is the worker thread object that performs IO on
// entries. It's owned by this SimpleEntryImpl whenever |executing_operation_|
@@ -298,6 +325,17 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
net::BoundNetLog net_log_;
scoped_ptr<SimpleEntryOperation> executing_operation_;
+
+ // Unlike other streams, stream 0 data is read from the disk when the entry is
+ // opened, and then kept in memory. All read/write operations on stream 0
+ // affect the |stream_0_data_| buffer. When the entry is closed,
+ // |stream_0_data_| is written to the disk.
+ // Stream 0 is kept in memory because it is stored in the same file as stream
+ // 1 on disk, to reduce the number of file descriptors and save disk space.
+ // This strategy allows stream 1 to change size easily. Since stream 0 is only
+ // used to write HTTP headers, the memory consumption of keeping it in memory
+ // is acceptable.
+ scoped_refptr<net::GrowableIOBuffer> stream_0_data_;
};
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc
index 61318c83b9..b8ec6d7e68 100644
--- a/net/disk_cache/simple/simple_index_file.cc
+++ b/net/disk_cache/simple/simple_index_file.cc
@@ -14,6 +14,7 @@
#include "base/single_thread_task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "net/disk_cache/simple/simple_histogram_macros.h"
#include "net/disk_cache/simple/simple_index.h"
@@ -43,36 +44,43 @@ void DoomEntrySetReply(const net::CompletionCallback& reply_callback,
reply_callback.Run(result);
}
-void WriteToDiskInternal(net::CacheType cache_type,
- const base::FilePath& index_filename,
- const base::FilePath& temp_index_filename,
- scoped_ptr<Pickle> pickle,
- const base::TimeTicks& start_time,
- bool app_on_background) {
+// Used in histograms. Please only add new values at the end.
+enum IndexFileState {
+ INDEX_STATE_CORRUPT = 0,
+ INDEX_STATE_STALE = 1,
+ INDEX_STATE_FRESH = 2,
+ INDEX_STATE_FRESH_CONCURRENT_UPDATES = 3,
+ INDEX_STATE_MAX = 4,
+};
+
+void UmaRecordIndexFileState(IndexFileState state, net::CacheType cache_type) {
+ SIMPLE_CACHE_UMA(ENUMERATION,
+ "IndexFileStateOnLoad", cache_type, state, INDEX_STATE_MAX);
+}
+
+// Used in histograms. Please only add new values at the end.
+enum IndexInitMethod {
+ INITIALIZE_METHOD_RECOVERED = 0,
+ INITIALIZE_METHOD_LOADED = 1,
+ INITIALIZE_METHOD_NEWCACHE = 2,
+ INITIALIZE_METHOD_MAX = 3,
+};
+
+void UmaRecordIndexInitMethod(IndexInitMethod method,
+ net::CacheType cache_type) {
+ SIMPLE_CACHE_UMA(ENUMERATION,
+ "IndexInitializeMethod", cache_type,
+ method, INITIALIZE_METHOD_MAX);
+}
+
+bool WritePickleFile(Pickle* pickle, const base::FilePath& file_name) {
int bytes_written = file_util::WriteFile(
- temp_index_filename,
- reinterpret_cast<const char*>(pickle->data()),
- pickle->size());
- DCHECK_EQ(bytes_written, implicit_cast<int>(pickle->size()));
- if (bytes_written != static_cast<int>(pickle->size())) {
- // TODO(felipeg): Add better error handling.
- LOG(ERROR) << "Could not write Simple Cache index to temporary file: "
- << temp_index_filename.value();
- base::DeleteFile(temp_index_filename, /* recursive = */ false);
- } else {
- // Swap temp and index_file.
- bool result = base::ReplaceFile(temp_index_filename, index_filename, NULL);
- DCHECK(result);
- }
- if (app_on_background) {
- SIMPLE_CACHE_UMA(TIMES,
- "IndexWriteToDiskTime.Background", cache_type,
- (base::TimeTicks::Now() - start_time));
- } else {
- SIMPLE_CACHE_UMA(TIMES,
- "IndexWriteToDiskTime.Foreground", cache_type,
- (base::TimeTicks::Now() - start_time));
+ file_name, static_cast<const char*>(pickle->data()), pickle->size());
+ if (bytes_written != implicit_cast<int>(pickle->size())) {
+ base::DeleteFile(file_name, /* recursive = */ false);
+ return false;
}
+ return true;
}
// Called for each cache directory traversal iteration.
@@ -117,7 +125,7 @@ void ProcessEntryFile(SimpleIndex::EntrySet* entries,
EntryMetadata(last_used_time, file_size),
entries);
} else {
- // Summing up the total size of the entry through all the *_[0-2] files
+ // Summing up the total size of the entry through all the *_[0-1] files
it->second.SetEntrySize(it->second.GetEntrySize() + file_size);
}
}
@@ -137,6 +145,13 @@ void SimpleIndexLoadResult::Reset() {
entries.clear();
}
+// static
+const char SimpleIndexFile::kIndexFileName[] = "the-real-index";
+// static
+const char SimpleIndexFile::kIndexDirectory[] = "index-dir";
+// static
+const char SimpleIndexFile::kTempIndexFileName[] = "temp-index";
+
SimpleIndexFile::IndexMetadata::IndexMetadata() :
magic_number_(kSimpleIndexMagicNumber),
version_(kSimpleVersion),
@@ -158,6 +173,16 @@ void SimpleIndexFile::IndexMetadata::Serialize(Pickle* pickle) const {
pickle->WriteUInt64(cache_size_);
}
+// static
+bool SimpleIndexFile::SerializeFinalData(base::Time cache_modified,
+ Pickle* pickle) {
+ if (!pickle->WriteInt64(cache_modified.ToInternalValue()))
+ return false;
+ SimpleIndexFile::PickleHeader* header_p = pickle->headerT<PickleHeader>();
+ header_p->crc = CalculatePickleCRC(*pickle);
+ return true;
+}
+
bool SimpleIndexFile::IndexMetadata::Deserialize(PickleIterator* it) {
DCHECK(it);
return it->ReadUInt64(&magic_number_) &&
@@ -166,10 +191,55 @@ bool SimpleIndexFile::IndexMetadata::Deserialize(PickleIterator* it) {
it->ReadUInt64(&cache_size_);
}
+void SimpleIndexFile::SyncWriteToDisk(net::CacheType cache_type,
+ const base::FilePath& cache_directory,
+ const base::FilePath& index_filename,
+ const base::FilePath& temp_index_filename,
+ scoped_ptr<Pickle> pickle,
+ const base::TimeTicks& start_time,
+ bool app_on_background) {
+ // There is a chance that the index containing all the necessary data about
+ // newly created entries will appear to be stale. This can happen if on-disk
+ // part of a Create operation does not fit into the time budget for the index
+ // flush delay. This simple approach will be reconsidered if it does not allow
+ // for maintaining freshness.
+ base::PlatformFileInfo cache_dir_info;
+ base::Time cache_dir_mtime;
+ if (!simple_util::GetMTime(cache_directory, &cache_dir_mtime)) {
+ LOG(ERROR) << "Could obtain information about cache age";
+ return;
+ }
+ SerializeFinalData(cache_dir_mtime, pickle.get());
+ if (!WritePickleFile(pickle.get(), temp_index_filename)) {
+ if (!file_util::CreateDirectory(temp_index_filename.DirName())) {
+ LOG(ERROR) << "Could not create a directory to hold the index file";
+ return;
+ }
+ if (!WritePickleFile(pickle.get(), temp_index_filename)) {
+ LOG(ERROR) << "Failed to write the temporary index file";
+ return;
+ }
+ }
+
+ // Atomically rename the temporary index file to become the real one.
+ bool result = base::ReplaceFile(temp_index_filename, index_filename, NULL);
+ DCHECK(result);
+
+ if (app_on_background) {
+ SIMPLE_CACHE_UMA(TIMES,
+ "IndexWriteToDiskTime.Background", cache_type,
+ (base::TimeTicks::Now() - start_time));
+ } else {
+ SIMPLE_CACHE_UMA(TIMES,
+ "IndexWriteToDiskTime.Foreground", cache_type,
+ (base::TimeTicks::Now() - start_time));
+ }
+}
+
bool SimpleIndexFile::IndexMetadata::CheckIndexMetadata() {
return number_of_entries_ <= kMaxEntiresInIndex &&
- magic_number_ == disk_cache::kSimpleIndexMagicNumber &&
- version_ == disk_cache::kSimpleVersion;
+ magic_number_ == kSimpleIndexMagicNumber &&
+ version_ == kSimpleVersion;
}
SimpleIndexFile::SimpleIndexFile(
@@ -181,8 +251,10 @@ SimpleIndexFile::SimpleIndexFile(
worker_pool_(worker_pool),
cache_type_(cache_type),
cache_directory_(cache_directory),
- index_file_(cache_directory_.AppendASCII(kIndexFileName)),
- temp_index_file_(cache_directory_.AppendASCII(kTempIndexFileName)) {
+ index_file_(cache_directory_.AppendASCII(kIndexDirectory)
+ .AppendASCII(kIndexFileName)),
+ temp_index_file_(cache_directory_.AppendASCII(kIndexDirectory)
+ .AppendASCII(kTempIndexFileName)) {
}
SimpleIndexFile::~SimpleIndexFile() {}
@@ -204,8 +276,9 @@ void SimpleIndexFile::WriteToDisk(const SimpleIndex::EntrySet& entry_set,
IndexMetadata index_metadata(entry_set.size(), cache_size);
scoped_ptr<Pickle> pickle = Serialize(index_metadata, entry_set);
cache_thread_->PostTask(FROM_HERE, base::Bind(
- &WriteToDiskInternal,
+ &SimpleIndexFile::SyncWriteToDisk,
cache_type_,
+ cache_directory_,
index_file_,
temp_index_file_,
base::Passed(&pickle),
@@ -231,84 +304,51 @@ void SimpleIndexFile::SyncLoadIndexEntries(
const base::FilePath& cache_directory,
const base::FilePath& index_file_path,
SimpleIndexLoadResult* out_result) {
- // TODO(felipeg): probably could load a stale index and use it for something.
- const SimpleIndex::EntrySet& entries = out_result->entries;
-
- const bool index_file_exists = base::PathExists(index_file_path);
-
- // Used in histograms. Please only add new values at the end.
- enum {
- INDEX_STATE_CORRUPT = 0,
- INDEX_STATE_STALE = 1,
- INDEX_STATE_FRESH = 2,
- INDEX_STATE_FRESH_CONCURRENT_UPDATES = 3,
- INDEX_STATE_MAX = 4,
- } index_file_state;
-
- // Only load if the index is not stale.
- if (IsIndexFileStale(cache_last_modified, index_file_path)) {
- index_file_state = INDEX_STATE_STALE;
- } else {
- index_file_state = INDEX_STATE_FRESH;
- base::Time latest_dir_mtime;
- if (simple_util::GetMTime(cache_directory, &latest_dir_mtime) &&
- IsIndexFileStale(latest_dir_mtime, index_file_path)) {
- // A file operation has updated the directory since we last looked at it
- // during backend initialization.
- index_file_state = INDEX_STATE_FRESH_CONCURRENT_UPDATES;
- }
-
- const base::TimeTicks start = base::TimeTicks::Now();
- SyncLoadFromDisk(index_file_path, out_result);
- SIMPLE_CACHE_UMA(TIMES,
- "IndexLoadTime", cache_type,
- base::TimeTicks::Now() - start);
- SIMPLE_CACHE_UMA(COUNTS,
- "IndexEntriesLoaded", cache_type,
- out_result->did_load ? entries.size() : 0);
- if (!out_result->did_load)
- index_file_state = INDEX_STATE_CORRUPT;
- }
- SIMPLE_CACHE_UMA(ENUMERATION,
- "IndexFileStateOnLoad", cache_type,
- index_file_state, INDEX_STATE_MAX);
+ // Load the index and find its age.
+ base::Time last_cache_seen_by_index;
+ SyncLoadFromDisk(index_file_path, &last_cache_seen_by_index, out_result);
+ // Consider the index loaded if it is fresh.
+ const bool index_file_existed = base::PathExists(index_file_path);
if (!out_result->did_load) {
- const base::TimeTicks start = base::TimeTicks::Now();
- SyncRestoreFromDisk(cache_directory, index_file_path, out_result);
- SIMPLE_CACHE_UMA(MEDIUM_TIMES,
- "IndexRestoreTime", cache_type,
- base::TimeTicks::Now() - start);
- SIMPLE_CACHE_UMA(COUNTS,
- "IndexEntriesRestored", cache_type, entries.size());
+ if (index_file_existed)
+ UmaRecordIndexFileState(INDEX_STATE_CORRUPT, cache_type);
+ } else {
+ if (cache_last_modified <= last_cache_seen_by_index) {
+ base::Time latest_dir_mtime;
+ simple_util::GetMTime(cache_directory, &latest_dir_mtime);
+ if (LegacyIsIndexFileStale(latest_dir_mtime, index_file_path)) {
+ UmaRecordIndexFileState(INDEX_STATE_FRESH_CONCURRENT_UPDATES,
+ cache_type);
+ } else {
+ UmaRecordIndexFileState(INDEX_STATE_FRESH, cache_type);
+ }
+ UmaRecordIndexInitMethod(INITIALIZE_METHOD_LOADED, cache_type);
+ return;
+ }
+ UmaRecordIndexFileState(INDEX_STATE_STALE, cache_type);
}
- // Used in histograms. Please only add new values at the end.
- enum {
- INITIALIZE_METHOD_RECOVERED = 0,
- INITIALIZE_METHOD_LOADED = 1,
- INITIALIZE_METHOD_NEWCACHE = 2,
- INITIALIZE_METHOD_MAX = 3,
- };
- int initialize_method;
- if (index_file_exists) {
- if (out_result->flush_required)
- initialize_method = INITIALIZE_METHOD_RECOVERED;
- else
- initialize_method = INITIALIZE_METHOD_LOADED;
+ // Reconstruct the index by scanning the disk for entries.
+ const base::TimeTicks start = base::TimeTicks::Now();
+ SyncRestoreFromDisk(cache_directory, index_file_path, out_result);
+ SIMPLE_CACHE_UMA(MEDIUM_TIMES, "IndexRestoreTime", cache_type,
+ base::TimeTicks::Now() - start);
+ SIMPLE_CACHE_UMA(COUNTS, "IndexEntriesRestored", cache_type,
+ out_result->entries.size());
+ if (index_file_existed) {
+ UmaRecordIndexInitMethod(INITIALIZE_METHOD_RECOVERED, cache_type);
} else {
+ UmaRecordIndexInitMethod(INITIALIZE_METHOD_NEWCACHE, cache_type);
SIMPLE_CACHE_UMA(COUNTS,
- "IndexCreatedEntryCount", cache_type, entries.size());
- initialize_method = INITIALIZE_METHOD_NEWCACHE;
+ "IndexCreatedEntryCount", cache_type,
+ out_result->entries.size());
}
-
- SIMPLE_CACHE_UMA(ENUMERATION,
- "IndexInitializeMethod", cache_type,
- initialize_method, INITIALIZE_METHOD_MAX);
}
// static
void SimpleIndexFile::SyncLoadFromDisk(const base::FilePath& index_filename,
+ base::Time* out_last_cache_seen_by_index,
SimpleIndexLoadResult* out_result) {
out_result->Reset();
@@ -321,7 +361,9 @@ void SimpleIndexFile::SyncLoadFromDisk(const base::FilePath& index_filename,
SimpleIndexFile::Deserialize(
reinterpret_cast<const char*>(index_file_map.data()),
- index_file_map.length(), out_result);
+ index_file_map.length(),
+ out_last_cache_seen_by_index,
+ out_result);
if (!out_result->did_load)
base::DeleteFile(index_filename, false);
@@ -339,14 +381,12 @@ scoped_ptr<Pickle> SimpleIndexFile::Serialize(
pickle->WriteUInt64(it->first);
it->second.Serialize(pickle.get());
}
- SimpleIndexFile::PickleHeader* header_p =
- pickle->headerT<SimpleIndexFile::PickleHeader>();
- header_p->crc = CalculatePickleCRC(*pickle);
return pickle.Pass();
}
// static
void SimpleIndexFile::Deserialize(const char* data, int data_len,
+ base::Time* out_cache_last_modified,
SimpleIndexLoadResult* out_result) {
DCHECK(data);
@@ -360,7 +400,6 @@ void SimpleIndexFile::Deserialize(const char* data, int data_len,
}
PickleIterator pickle_it(pickle);
-
SimpleIndexFile::PickleHeader* header_p =
pickle.headerT<SimpleIndexFile::PickleHeader>();
const uint32 crc_read = header_p->crc;
@@ -398,6 +437,14 @@ void SimpleIndexFile::Deserialize(const char* data, int data_len,
SimpleIndex::InsertInEntrySet(hash_key, entry_metadata, entries);
}
+ int64 cache_last_modified;
+ if (!pickle_it.ReadInt64(&cache_last_modified)) {
+ entries->clear();
+ return;
+ }
+ DCHECK(out_cache_last_modified);
+ *out_cache_last_modified = base::Time::FromInternalValue(cache_last_modified);
+
out_result->did_load = true;
}
@@ -411,10 +458,6 @@ void SimpleIndexFile::SyncRestoreFromDisk(
out_result->Reset();
SimpleIndex::EntrySet* entries = &out_result->entries;
- // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format.
- COMPILE_ASSERT(kSimpleEntryFileCount == 3,
- file_pattern_must_match_file_count);
-
const bool did_succeed = TraverseCacheDirectory(
cache_directory, base::Bind(&ProcessEntryFile, entries));
if (!did_succeed) {
@@ -428,8 +471,9 @@ void SimpleIndexFile::SyncRestoreFromDisk(
}
// static
-bool SimpleIndexFile::IsIndexFileStale(base::Time cache_last_modified,
- const base::FilePath& index_file_path) {
+bool SimpleIndexFile::LegacyIsIndexFileStale(
+ base::Time cache_last_modified,
+ const base::FilePath& index_file_path) {
base::Time index_mtime;
if (!simple_util::GetMTime(index_file_path, &index_mtime))
return true;
diff --git a/net/disk_cache/simple/simple_index_file.h b/net/disk_cache/simple/simple_index_file.h
index 7c7a34e536..b72ea9f749 100644
--- a/net/disk_cache/simple/simple_index_file.h
+++ b/net/disk_cache/simple/simple_index_file.h
@@ -113,20 +113,29 @@ class NET_EXPORT_PRIVATE SimpleIndexFile {
const base::FilePath& index_file_path,
SimpleIndexLoadResult* out_result);
- // Load the index file from disk returning an EntrySet. Upon failure, returns
- // NULL.
+ // Load the index file from disk returning an EntrySet.
static void SyncLoadFromDisk(const base::FilePath& index_filename,
+ base::Time* out_last_cache_seen_by_index,
SimpleIndexLoadResult* out_result);
// Returns a scoped_ptr for a newly allocated Pickle containing the serialized
- // data to be written to a file.
+ // data to be written to a file. Note: the pickle is not in a consistent state
+ // immediately after calling this menthod, one needs to call
+ // SerializeFinalData to make it ready to write to a file.
static scoped_ptr<Pickle> Serialize(
const SimpleIndexFile::IndexMetadata& index_metadata,
const SimpleIndex::EntrySet& entries);
+ // Appends cache modification time data to the serialized format. This is
+ // performed on a thread accessing the disk. It is not combined with the main
+ // serialization path to avoid extra thread hops or copying the pickle to the
+ // worker thread.
+ static bool SerializeFinalData(base::Time cache_modified, Pickle* pickle);
+
// Given the contents of an index file |data| of length |data_len|, returns
// the corresponding EntrySet. Returns NULL on error.
static void Deserialize(const char* data, int data_len,
+ base::Time* out_cache_last_modified,
SimpleIndexLoadResult* out_result);
// Implemented either in simple_index_file_posix.cc or
@@ -138,6 +147,15 @@ class NET_EXPORT_PRIVATE SimpleIndexFile {
const base::FilePath& cache_path,
const EntryFileCallback& entry_file_callback);
+ // Writes the index file to disk atomically.
+ static void SyncWriteToDisk(net::CacheType cache_type,
+ const base::FilePath& cache_directory,
+ const base::FilePath& index_filename,
+ const base::FilePath& temp_index_filename,
+ scoped_ptr<Pickle> pickle,
+ const base::TimeTicks& start_time,
+ bool app_on_background);
+
// Scan the index directory for entries, returning an EntrySet of all entries
// found.
static void SyncRestoreFromDisk(const base::FilePath& cache_directory,
@@ -145,9 +163,11 @@ class NET_EXPORT_PRIVATE SimpleIndexFile {
SimpleIndexLoadResult* out_result);
// Determines if an index file is stale relative to the time of last
- // modification of the cache directory.
- static bool IsIndexFileStale(base::Time cache_last_modified,
- const base::FilePath& index_file_path);
+ // modification of the cache directory. Obsolete, used only for a histogram to
+ // compare with the new method.
+ // TODO(pasko): remove this method after getting enough data.
+ static bool LegacyIsIndexFileStale(base::Time cache_last_modified,
+ const base::FilePath& index_file_path);
struct PickleHeader : public Pickle::Header {
uint32 crc;
@@ -160,6 +180,10 @@ class NET_EXPORT_PRIVATE SimpleIndexFile {
const base::FilePath index_file_;
const base::FilePath temp_index_file_;
+ static const char kIndexDirectory[];
+ static const char kIndexFileName[];
+ static const char kTempIndexFileName[];
+
DISALLOW_COPY_AND_ASSIGN(SimpleIndexFile);
};
diff --git a/net/disk_cache/simple/simple_index_file_unittest.cc b/net/disk_cache/simple/simple_index_file_unittest.cc
index b75237e8d2..267c6ba593 100644
--- a/net/disk_cache/simple/simple_index_file_unittest.cc
+++ b/net/disk_cache/simple/simple_index_file_unittest.cc
@@ -13,6 +13,7 @@
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "net/base/cache_type.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "net/disk_cache/simple/simple_index.h"
#include "net/disk_cache/simple/simple_index_file.h"
@@ -25,6 +26,11 @@ using disk_cache::SimpleIndex;
namespace disk_cache {
+// The Simple Cache backend requires a few guarantees from the filesystem like
+// atomic renaming of recently open files. Those guarantees are not provided in
+// general on Windows.
+#if defined(OS_POSIX)
+
TEST(IndexMetadataTest, Basics) {
SimpleIndexFile::IndexMetadata index_metadata;
@@ -58,8 +64,9 @@ TEST(IndexMetadataTest, Serialize) {
class WrappedSimpleIndexFile : public SimpleIndexFile {
public:
using SimpleIndexFile::Deserialize;
- using SimpleIndexFile::IsIndexFileStale;
+ using SimpleIndexFile::LegacyIsIndexFileStale;
using SimpleIndexFile::Serialize;
+ using SimpleIndexFile::SerializeFinalData;
explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory)
: SimpleIndexFile(base::MessageLoopProxy::current().get(),
@@ -72,6 +79,10 @@ class WrappedSimpleIndexFile : public SimpleIndexFile {
const base::FilePath& GetIndexFilePath() const {
return index_file_;
}
+
+ bool CreateIndexFileDirectory() const {
+ return file_util::CreateDirectory(index_file_.DirName());
+ }
};
class SimpleIndexFileTest : public testing::Test {
@@ -119,12 +130,16 @@ TEST_F(SimpleIndexFileTest, Serialize) {
scoped_ptr<Pickle> pickle = WrappedSimpleIndexFile::Serialize(
index_metadata, entries);
EXPECT_TRUE(pickle.get() != NULL);
-
+ base::Time now = base::Time::Now();
+ EXPECT_TRUE(WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get()));
+ base::Time when_index_last_saw_cache;
SimpleIndexLoadResult deserialize_result;
WrappedSimpleIndexFile::Deserialize(static_cast<const char*>(pickle->data()),
- pickle->size(),
- &deserialize_result);
+ pickle->size(),
+ &when_index_last_saw_cache,
+ &deserialize_result);
EXPECT_TRUE(deserialize_result.did_load);
+ EXPECT_EQ(now, when_index_last_saw_cache);
const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
EXPECT_EQ(entries.size(), new_entries.size());
@@ -135,7 +150,7 @@ TEST_F(SimpleIndexFileTest, Serialize) {
}
}
-TEST_F(SimpleIndexFileTest, IsIndexFileStale) {
+TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) {
base::ScopedTempDir cache_dir;
ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
base::Time cache_mtime;
@@ -143,31 +158,30 @@ TEST_F(SimpleIndexFileTest, IsIndexFileStale) {
ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
WrappedSimpleIndexFile simple_index_file(cache_path);
+ ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
- EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
- index_path));
+ EXPECT_TRUE(
+ WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
const std::string kDummyData = "nothing to be seen here";
EXPECT_EQ(static_cast<int>(kDummyData.size()),
file_util::WriteFile(index_path,
kDummyData.data(),
kDummyData.size()));
ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
- EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
- index_path));
+ EXPECT_FALSE(
+ WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
const base::Time past_time = base::Time::Now() -
base::TimeDelta::FromSeconds(10);
EXPECT_TRUE(file_util::TouchFile(index_path, past_time, past_time));
EXPECT_TRUE(file_util::TouchFile(cache_path, past_time, past_time));
ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
- EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
- index_path));
- const base::Time even_older =
- past_time - base::TimeDelta::FromSeconds(10);
+ EXPECT_FALSE(
+ WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
+ const base::Time even_older = past_time - base::TimeDelta::FromSeconds(10);
EXPECT_TRUE(file_util::TouchFile(index_path, even_older, even_older));
- EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
- index_path));
-
+ EXPECT_TRUE(
+ WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
}
TEST_F(SimpleIndexFileTest, WriteThenLoadIndex) {
@@ -218,17 +232,17 @@ TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
WrappedSimpleIndexFile simple_index_file(cache_dir.path());
+ ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
const std::string kDummyData = "nothing to be seen here";
- EXPECT_EQ(static_cast<int>(kDummyData.size()),
- file_util::WriteFile(index_path,
- kDummyData.data(),
- kDummyData.size()));
+ EXPECT_EQ(
+ implicit_cast<int>(kDummyData.size()),
+ file_util::WriteFile(index_path, kDummyData.data(), kDummyData.size()));
base::Time fake_cache_mtime;
ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
&fake_cache_mtime));
- EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(fake_cache_mtime,
- index_path));
+ EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
+ index_path));
SimpleIndexLoadResult load_index_result;
simple_index_file.LoadIndexEntries(fake_cache_mtime,
@@ -242,4 +256,6 @@ TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
EXPECT_TRUE(load_index_result.flush_required);
}
+#endif // defined(OS_POSIX)
+
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index a1ff1ac6ad..3b1f7a86ab 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -18,6 +18,7 @@
#include "base/strings/stringprintf.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
#include "net/disk_cache/simple/simple_histogram_macros.h"
#include "net/disk_cache/simple/simple_util.h"
#include "third_party/zlib/zlib.h"
@@ -141,27 +142,55 @@ void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
namespace disk_cache {
-using simple_util::ConvertEntryHashKeyToHexString;
using simple_util::GetEntryHashKey;
-using simple_util::GetFilenameFromEntryHashAndIndex;
+using simple_util::GetFilenameFromEntryHashAndFileIndex;
using simple_util::GetDataSizeFromKeyAndFileSize;
using simple_util::GetFileSizeFromKeyAndDataSize;
-using simple_util::GetFileOffsetFromKeyAndDataOffset;
+using simple_util::GetFileIndexFromStreamIndex;
+
+SimpleEntryStat::SimpleEntryStat(base::Time last_used,
+ base::Time last_modified,
+ const int32 data_size[])
+ : last_used_(last_used),
+ last_modified_(last_modified) {
+ memcpy(data_size_, data_size, sizeof(data_size_));
+}
-SimpleEntryStat::SimpleEntryStat() {}
+int SimpleEntryStat::GetOffsetInFile(const std::string& key,
+ int offset,
+ int stream_index) const {
+ const int64 headers_size = sizeof(SimpleFileHeader) + key.size();
+ const int64 additional_offset =
+ stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0;
+ return headers_size + offset + additional_offset;
+}
-SimpleEntryStat::SimpleEntryStat(base::Time last_used_p,
- base::Time last_modified_p,
- const int32 data_size_p[])
- : last_used(last_used_p),
- last_modified(last_modified_p) {
- memcpy(data_size, data_size_p, sizeof(data_size));
+int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key,
+ int stream_index) const {
+ return GetOffsetInFile(key, data_size_[stream_index], stream_index);
+}
+
+int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key,
+ int stream_index) const {
+ const int file_index = GetFileIndexFromStreamIndex(stream_index);
+ const int eof_data_offset =
+ file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
+ : data_size_[2];
+ return GetOffsetInFile(key, eof_data_offset, stream_index);
+}
+
+int SimpleEntryStat::GetFileSize(const std::string& key, int file_index) const {
+ const int total_data_size =
+ file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
+ : data_size_[2];
+ return GetFileSizeFromKeyAndDataSize(key, total_data_size);
}
SimpleEntryCreationResults::SimpleEntryCreationResults(
SimpleEntryStat entry_stat)
: sync_entry(NULL),
entry_stat(entry_stat),
+ stream_0_crc32(crc32(0, Z_NULL, 0)),
result(net::OK) {
}
@@ -205,12 +234,16 @@ void SimpleSynchronousEntry::OpenEntry(
SimpleEntryCreationResults *out_results) {
SimpleSynchronousEntry* sync_entry =
new SimpleSynchronousEntry(cache_type, path, "", entry_hash);
- out_results->result = sync_entry->InitializeForOpen(
- had_index, &out_results->entry_stat);
+ out_results->result =
+ sync_entry->InitializeForOpen(had_index,
+ &out_results->entry_stat,
+ &out_results->stream_0_data,
+ &out_results->stream_0_crc32);
if (out_results->result != net::OK) {
sync_entry->Doom();
delete sync_entry;
out_results->sync_entry = NULL;
+ out_results->stream_0_data = NULL;
return;
}
out_results->sync_entry = sync_entry;
@@ -246,8 +279,8 @@ bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
const uint64 entry_hash) {
bool result = true;
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- FilePath to_delete = path.AppendASCII(
- GetFilenameFromEntryHashAndIndex(entry_hash, i));
+ FilePath to_delete =
+ path.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash, i));
if (!base::DeleteFile(to_delete, false)) {
result = false;
DLOG(ERROR) << "Could not delete " << to_delete.MaybeAsASCII();
@@ -279,17 +312,17 @@ int SimpleSynchronousEntry::DoomEntrySet(
void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op,
net::IOBuffer* out_buf,
uint32* out_crc32,
- base::Time* out_last_used,
+ SimpleEntryStat* entry_stat,
int* out_result) const {
DCHECK(initialized_);
- int64 file_offset =
- GetFileOffsetFromKeyAndDataOffset(key_, in_entry_op.offset);
- int bytes_read = ReadPlatformFile(files_[in_entry_op.index],
- file_offset,
- out_buf->data(),
- in_entry_op.buf_len);
+ DCHECK_NE(0, in_entry_op.index);
+ const int64 file_offset =
+ entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index);
+ int file_index = GetFileIndexFromStreamIndex(in_entry_op.index);
+ int bytes_read = ReadPlatformFile(
+ files_[file_index], file_offset, out_buf->data(), in_entry_op.buf_len);
if (bytes_read > 0) {
- *out_last_used = Time::Now();
+ entry_stat->set_last_used(Time::Now());
*out_crc32 = crc32(crc32(0L, Z_NULL, 0),
reinterpret_cast<const Bytef*>(out_buf->data()),
bytes_read);
@@ -307,27 +340,30 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
SimpleEntryStat* out_entry_stat,
int* out_result) const {
DCHECK(initialized_);
+ DCHECK_NE(0, in_entry_op.index);
int index = in_entry_op.index;
+ int file_index = GetFileIndexFromStreamIndex(index);
int offset = in_entry_op.offset;
int buf_len = in_entry_op.buf_len;
int truncate = in_entry_op.truncate;
-
- bool extending_by_write = offset + buf_len > out_entry_stat->data_size[index];
+ const int64 file_offset = out_entry_stat->GetOffsetInFile(
+ key_, in_entry_op.offset, in_entry_op.index);
+ bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index);
if (extending_by_write) {
- // We are extending the file, and need to insure the EOF record is zeroed.
- const int64 file_eof_offset = GetFileOffsetFromKeyAndDataOffset(
- key_, out_entry_stat->data_size[index]);
- if (!TruncatePlatformFile(files_[index], file_eof_offset)) {
+ // The EOF record and the eventual stream afterward need to be zeroed out.
+ const int64 file_eof_offset =
+ out_entry_stat->GetEOFOffsetInFile(key_, index);
+ if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
return;
}
}
- const int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, offset);
if (buf_len > 0) {
if (WritePlatformFile(
- files_[index], file_offset, in_buf->data(), buf_len) != buf_len) {
+ files_[file_index], file_offset, in_buf->data(), buf_len) !=
+ buf_len) {
RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
@@ -335,79 +371,89 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
}
}
if (!truncate && (buf_len > 0 || !extending_by_write)) {
- out_entry_stat->data_size[index] =
- std::max(out_entry_stat->data_size[index], offset + buf_len);
+ out_entry_stat->set_data_size(
+ index, std::max(out_entry_stat->data_size(index), offset + buf_len));
} else {
- if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) {
+ out_entry_stat->set_data_size(index, offset + buf_len);
+ int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index);
+ if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
return;
}
- out_entry_stat->data_size[index] = offset + buf_len;
}
RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
- out_entry_stat->last_used = out_entry_stat->last_modified = Time::Now();
+ base::Time modification_time = Time::Now();
+ out_entry_stat->set_last_used(modification_time);
+ out_entry_stat->set_last_modified(modification_time);
*out_result = buf_len;
}
void SimpleSynchronousEntry::CheckEOFRecord(int index,
- int32 data_size,
+ const SimpleEntryStat& entry_stat,
uint32 expected_crc32,
int* out_result) const {
DCHECK(initialized_);
-
- SimpleFileEOF eof_record;
- int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, data_size);
- if (ReadPlatformFile(files_[index],
- file_offset,
- reinterpret_cast<char*>(&eof_record),
- sizeof(eof_record)) != sizeof(eof_record)) {
- RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
- Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE;
- return;
- }
-
- if (eof_record.final_magic_number != kSimpleFinalMagicNumber) {
- RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
- DLOG(INFO) << "eof record had bad magic number.";
+ uint32 crc32;
+ bool has_crc32;
+ int stream_size;
+ *out_result =
+ GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size);
+ if (*out_result != net::OK) {
Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE;
return;
}
-
- const bool has_crc = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) ==
- SimpleFileEOF::FLAG_HAS_CRC32;
- SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, has_crc);
- if (has_crc && eof_record.data_crc32 != expected_crc32) {
+ if (has_crc32 && crc32 != expected_crc32) {
+ DLOG(INFO) << "EOF record had bad crc.";
+ *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
- DLOG(INFO) << "eof record had bad crc.";
Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
return;
}
-
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
- *out_result = net::OK;
}
void SimpleSynchronousEntry::Close(
const SimpleEntryStat& entry_stat,
- scoped_ptr<std::vector<CRCRecord> > crc32s_to_write) {
+ scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
+ net::GrowableIOBuffer* stream_0_data) {
+ DCHECK(stream_0_data);
+ // Write stream 0 data.
+ int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0);
+ if (WritePlatformFile(files_[0],
+ stream_0_offset,
+ stream_0_data->data(),
+ entry_stat.data_size(0)) != entry_stat.data_size(0)) {
+ RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
+ DLOG(INFO) << "Could not write stream 0 data.";
+ Doom();
+ }
+
for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin();
it != crc32s_to_write->end(); ++it) {
SimpleFileEOF eof_record;
+ int index = it->index;
+ eof_record.stream_size = entry_stat.data_size(index);
eof_record.final_magic_number = kSimpleFinalMagicNumber;
eof_record.flags = 0;
if (it->has_crc32)
eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32;
eof_record.data_crc32 = it->data_crc32;
- int64 file_offset = GetFileOffsetFromKeyAndDataOffset(
- key_, entry_stat.data_size[it->index]);
- if (WritePlatformFile(files_[it->index],
- file_offset,
+ int file_index = GetFileIndexFromStreamIndex(index);
+ int eof_offset = entry_stat.GetEOFOffsetInFile(key_, index);
+ // If stream 0 changed size, the file needs to be resized, otherwise the
+ // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
+ // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
+ if (index == 0 && !TruncatePlatformFile(files_[file_index], eof_offset)) {
+ RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
+ DLOG(INFO) << "Could not truncate stream 0 file.";
+ Doom();
+ break;
+ }
+ if (WritePlatformFile(files_[file_index],
+ eof_offset,
reinterpret_cast<const char*>(&eof_record),
sizeof(eof_record)) != sizeof(eof_record)) {
RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
@@ -415,7 +461,11 @@ void SimpleSynchronousEntry::Close(
Doom();
break;
}
- const int64 file_size = file_offset + sizeof(eof_record);
+ }
+ for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ bool did_close_file = ClosePlatformFile(files_[i]);
+ DCHECK(did_close_file);
+ const int64 file_size = entry_stat.GetFileSize(key_, i);
SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
"LastClusterSize", cache_type_,
file_size % 4096, 0, 4097, 50);
@@ -424,11 +474,6 @@ void SimpleSynchronousEntry::Close(
"LastClusterLossPercent", cache_type_,
cluster_loss * 100 / (cluster_loss + file_size));
}
-
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- bool did_close_file = ClosePlatformFile(files_[i]);
- CHECK(did_close_file);
- }
RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS);
have_open_files_ = false;
delete this;
@@ -460,8 +505,8 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
bool had_index,
SimpleEntryStat* out_entry_stat) {
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- FilePath filename = path_.AppendASCII(
- GetFilenameFromEntryHashAndIndex(entry_hash_, i));
+ FilePath filename =
+ path_.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash_, i));
int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
if (create)
flags |= PLATFORM_FILE_CREATE;
@@ -516,9 +561,11 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
have_open_files_ = true;
if (create) {
- out_entry_stat->last_modified = out_entry_stat->last_used = Time::Now();
- for (int i = 0; i < kSimpleEntryFileCount; ++i)
- out_entry_stat->data_size[i] = 0;
+ base::Time creation_time = Time::Now();
+ out_entry_stat->set_last_modified(creation_time);
+ out_entry_stat->set_last_used(creation_time);
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i)
+ out_entry_stat->set_data_size(i, 0);
} else {
base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch();
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
@@ -529,20 +576,31 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
DLOG(WARNING) << "Could not get platform file info.";
continue;
}
- out_entry_stat->last_used = file_info.last_accessed;
+ out_entry_stat->set_last_used(file_info.last_accessed);
if (simple_util::GetMTime(path_, &file_last_modified))
- out_entry_stat->last_modified = file_last_modified;
+ out_entry_stat->set_last_modified(file_last_modified);
else
- out_entry_stat->last_modified = file_info.last_modified;
+ out_entry_stat->set_last_modified(file_info.last_modified);
base::TimeDelta stream_age =
- base::Time::Now() - out_entry_stat->last_modified;
+ base::Time::Now() - out_entry_stat->last_modified();
if (stream_age < entry_age)
entry_age = stream_age;
- // Keep the file size in |data size_| briefly until the key is initialized
- // properly.
- out_entry_stat->data_size[i] = file_info.size;
+ // Two things prevent from knowing the right values for |data_size|:
+ // 1) The key is not known, hence its length is unknown.
+ // 2) Stream 0 and stream 1 are in the same file, and the exact size for
+ // each will only be known when reading the EOF record for stream 0.
+ //
+ // The size for file 0 and 1 is temporarily kept in
+ // |data_size(1)| and |data_size(2)| respectively. Reading the key in
+ // InitializeForOpen yields the data size for each file. In the case of
+ // file hash_1, this is the total size of stream 2, and is assigned to
+ // data_size(2). In the case of file 0, it is the combined size of stream
+ // 0, stream 1 and one EOF record. The exact distribution of sizes between
+ // stream 1 and stream 0 is only determined after reading the EOF record
+ // for stream 0 in ReadAndValidateStream0.
+ out_entry_stat->set_data_size(i + 1, file_info.size);
}
SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
"SyncOpenEntryAge", cache_type_,
@@ -560,12 +618,14 @@ void SimpleSynchronousEntry::CloseFiles() {
}
}
-int SimpleSynchronousEntry::InitializeForOpen(bool had_index,
- SimpleEntryStat* out_entry_stat) {
+int SimpleSynchronousEntry::InitializeForOpen(
+ bool had_index,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) {
DCHECK(!initialized_);
if (!OpenOrCreateFiles(false, had_index, out_entry_stat))
return net::ERR_FAILED;
-
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
SimpleFileHeader header;
int header_read_result =
@@ -586,7 +646,7 @@ int SimpleSynchronousEntry::InitializeForOpen(bool had_index,
return net::ERR_FAILED;
}
- if (header.version != kSimpleVersion) {
+ if (header.version != kSimpleEntryVersionOnDisk) {
DLOG(WARNING) << "Unreadable version.";
RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_VERSION, had_index);
return net::ERR_FAILED;
@@ -602,12 +662,19 @@ int SimpleSynchronousEntry::InitializeForOpen(bool had_index,
}
key_ = std::string(key.get(), header.key_length);
- out_entry_stat->data_size[i] =
- GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size[i]);
- if (out_entry_stat->data_size[i] < 0) {
- // This entry can't possibly be valid, as it does not have enough space to
- // store a valid SimpleFileEOF record.
- return net::ERR_FAILED;
+ if (i == 0) {
+ // File size for stream 0 has been stored temporarily in data_size[1].
+ int total_data_size =
+ GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(1));
+ int ret_value_stream_0 = ReadAndValidateStream0(
+ total_data_size, out_entry_stat, stream_0_data, out_stream_0_crc32);
+ if (ret_value_stream_0 != net::OK)
+ return ret_value_stream_0;
+ } else {
+ out_entry_stat->set_data_size(
+ 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2)));
+ if (out_entry_stat->data_size(2) < 0)
+ return net::ERR_FAILED;
}
if (base::Hash(key.get(), header.key_length) != header.key_hash) {
@@ -633,21 +700,23 @@ int SimpleSynchronousEntry::InitializeForCreate(
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
SimpleFileHeader header;
header.initial_magic_number = kSimpleInitialMagicNumber;
- header.version = kSimpleVersion;
+ header.version = kSimpleEntryVersionOnDisk;
header.key_length = key_.size();
header.key_hash = base::Hash(key_);
- if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
- sizeof(header)) != sizeof(header)) {
- DLOG(WARNING) << "Could not write headers to new cache entry.";
+ if (WritePlatformFile(
+ files_[i], 0, reinterpret_cast<char*>(&header), sizeof(header)) !=
+ sizeof(header)) {
+ DLOG(WARNING) << "Could not write cache file header to cache entry.";
RecordSyncCreateResult(
cache_type_, CREATE_ENTRY_CANT_WRITE_HEADER, had_index);
return net::ERR_FAILED;
}
- if (WritePlatformFile(files_[i], sizeof(header), key_.data(),
- key_.size()) != implicit_cast<int>(key_.size())) {
+ if (WritePlatformFile(
+ files_[i], sizeof(SimpleFileHeader), key_.data(), key_.size()) !=
+ implicit_cast<int>(key_.size())) {
DLOG(WARNING) << "Could not write keys to new cache entry.";
RecordSyncCreateResult(
cache_type_, CREATE_ENTRY_CANT_WRITE_KEY, had_index);
@@ -659,6 +728,88 @@ int SimpleSynchronousEntry::InitializeForCreate(
return net::OK;
}
+int SimpleSynchronousEntry::ReadAndValidateStream0(
+ int total_data_size,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) const {
+ // Temporarily assign all the data size to stream 1 in order to read the
+ // EOF record for stream 0, which contains the size of stream 0.
+ out_entry_stat->set_data_size(0, 0);
+ out_entry_stat->set_data_size(1, total_data_size - sizeof(SimpleFileEOF));
+
+ bool has_crc32;
+ uint32 read_crc32;
+ int stream_0_size;
+ int ret_value_crc32 = GetEOFRecordData(
+ 0, *out_entry_stat, &has_crc32, &read_crc32, &stream_0_size);
+ if (ret_value_crc32 != net::OK)
+ return ret_value_crc32;
+
+ if (stream_0_size > out_entry_stat->data_size(1))
+ return net::ERR_FAILED;
+
+ // These are the real values of data size.
+ out_entry_stat->set_data_size(0, stream_0_size);
+ out_entry_stat->set_data_size(
+ 1, out_entry_stat->data_size(1) - stream_0_size);
+
+ // Put stream 0 data in memory.
+ *stream_0_data = new net::GrowableIOBuffer();
+ (*stream_0_data)->SetCapacity(stream_0_size);
+ int file_offset = out_entry_stat->GetOffsetInFile(key_, 0, 0);
+ int bytes_read = ReadPlatformFile(
+ files_[0], file_offset, (*stream_0_data)->data(), stream_0_size);
+ if (bytes_read != stream_0_size)
+ return net::ERR_FAILED;
+
+ // Check the CRC32.
+ uint32 expected_crc32 =
+ stream_0_size == 0
+ ? crc32(0, Z_NULL, 0)
+ : crc32(crc32(0, Z_NULL, 0),
+ reinterpret_cast<const Bytef*>((*stream_0_data)->data()),
+ stream_0_size);
+ if (has_crc32 && read_crc32 != expected_crc32) {
+ DLOG(INFO) << "EOF record had bad crc.";
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
+ return net::ERR_FAILED;
+ }
+ *out_stream_0_crc32 = expected_crc32;
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
+ return net::OK;
+}
+
+int SimpleSynchronousEntry::GetEOFRecordData(int index,
+ const SimpleEntryStat& entry_stat,
+ bool* out_has_crc32,
+ uint32* out_crc32,
+ int* out_data_size) const {
+ SimpleFileEOF eof_record;
+ int file_offset = entry_stat.GetEOFOffsetInFile(key_, index);
+ int file_index = GetFileIndexFromStreamIndex(index);
+ if (ReadPlatformFile(files_[file_index],
+ file_offset,
+ reinterpret_cast<char*>(&eof_record),
+ sizeof(eof_record)) != sizeof(eof_record)) {
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
+ return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
+ }
+
+ if (eof_record.final_magic_number != kSimpleFinalMagicNumber) {
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
+ DLOG(INFO) << "EOF record had bad magic number.";
+ return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
+ }
+
+ *out_has_crc32 = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) ==
+ SimpleFileEOF::FLAG_HAS_CRC32;
+ *out_crc32 = eof_record.data_crc32;
+ *out_data_size = eof_record.stream_size;
+ SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, *out_has_crc32);
+ return net::OK;
+}
+
void SimpleSynchronousEntry::Doom() const {
// TODO(gavinp): Consider if we should guard against redundant Doom() calls.
DeleteFilesForEntryHash(path_, entry_hash_);
diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h
index f4b5ed8102..ae270c31c1 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.h
+++ b/net/disk_cache/simple/simple_synchronous_entry.h
@@ -11,13 +11,16 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/platform_file.h"
#include "base/time/time.h"
#include "net/base/cache_type.h"
+#include "net/base/net_export.h"
#include "net/disk_cache/simple/simple_entry_format.h"
namespace net {
+class GrowableIOBuffer;
class IOBuffer;
}
@@ -25,15 +28,38 @@ namespace disk_cache {
class SimpleSynchronousEntry;
-struct SimpleEntryStat {
- SimpleEntryStat();
- SimpleEntryStat(base::Time last_used_p,
- base::Time last_modified_p,
- const int32 data_size_p[]);
+// This class handles the passing of data about the entry between
+// SimpleEntryImplementation and SimpleSynchronousEntry and the computation of
+// file offsets based on the data size for all streams.
+class NET_EXPORT_PRIVATE SimpleEntryStat {
+ public:
+ SimpleEntryStat(base::Time last_used,
+ base::Time last_modified,
+ const int32 data_size[]);
+
+ int GetOffsetInFile(const std::string& key,
+ int offset,
+ int stream_index) const;
+ int GetEOFOffsetInFile(const std::string& key, int stream_index) const;
+ int GetLastEOFOffsetInFile(const std::string& key, int file_index) const;
+ int GetFileSize(const std::string& key, int file_index) const;
+
+ base::Time last_used() const { return last_used_; }
+ base::Time last_modified() const { return last_modified_; }
+ void set_last_used(base::Time last_used) { last_used_ = last_used; }
+ void set_last_modified(base::Time last_modified) {
+ last_modified_ = last_modified;
+ }
+
+ int32 data_size(int stream_index) const { return data_size_[stream_index]; }
+ void set_data_size(int stream_index, int data_size) {
+ data_size_[stream_index] = data_size;
+ }
- base::Time last_used;
- base::Time last_modified;
- int32 data_size[kSimpleEntryFileCount];
+ private:
+ base::Time last_used_;
+ base::Time last_modified_;
+ int32 data_size_[kSimpleEntryStreamCount];
};
struct SimpleEntryCreationResults {
@@ -41,7 +67,9 @@ struct SimpleEntryCreationResults {
~SimpleEntryCreationResults();
SimpleSynchronousEntry* sync_entry;
+ scoped_refptr<net::GrowableIOBuffer> stream_0_data;
SimpleEntryStat entry_stat;
+ uint32 stream_0_crc32;
int result;
};
@@ -102,21 +130,22 @@ class SimpleSynchronousEntry {
void ReadData(const EntryOperationData& in_entry_op,
net::IOBuffer* out_buf,
uint32* out_crc32,
- base::Time* out_last_used,
+ SimpleEntryStat* entry_stat,
int* out_result) const;
void WriteData(const EntryOperationData& in_entry_op,
net::IOBuffer* in_buf,
SimpleEntryStat* out_entry_stat,
int* out_result) const;
void CheckEOFRecord(int index,
- int data_size,
+ const SimpleEntryStat& entry_stat,
uint32 expected_crc32,
int* out_result) const;
// Close all streams, and add write EOF records to streams indicated by the
// CRCRecord entries in |crc32s_to_write|.
void Close(const SimpleEntryStat& entry_stat,
- scoped_ptr<std::vector<CRCRecord> > crc32s_to_write);
+ scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
+ net::GrowableIOBuffer* stream_0_data);
const base::FilePath& path() const { return path_; }
std::string key() const { return key_; }
@@ -140,7 +169,10 @@ class SimpleSynchronousEntry {
// Returns a net error, i.e. net::OK on success. |had_index| is passed
// from the main entry for metrics purposes, and is true if the index was
// initialized when the open operation began.
- int InitializeForOpen(bool had_index, SimpleEntryStat* out_entry_stat);
+ int InitializeForOpen(bool had_index,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32);
// Returns a net error, including net::OK on success and net::FILE_EXISTS
// when the entry already exists. |had_index| is passed from the main entry
@@ -148,6 +180,19 @@ class SimpleSynchronousEntry {
// create operation began.
int InitializeForCreate(bool had_index, SimpleEntryStat* out_entry_stat);
+ // Allocates and fills a buffer with stream 0 data in |stream_0_data|, then
+ // checks its crc32.
+ int ReadAndValidateStream0(
+ int total_data_size,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) const;
+
+ int GetEOFRecordData(int index,
+ const SimpleEntryStat& entry_stat,
+ bool* out_has_crc32,
+ uint32* out_crc32,
+ int* out_data_size) const;
void Doom() const;
static bool DeleteFilesForEntryHash(const base::FilePath& path,
diff --git a/net/disk_cache/simple/simple_test_util.cc b/net/disk_cache/simple/simple_test_util.cc
index 9f09974c3b..9a27558823 100644
--- a/net/disk_cache/simple/simple_test_util.cc
+++ b/net/disk_cache/simple/simple_test_util.cc
@@ -13,7 +13,7 @@ namespace simple_util {
bool CreateCorruptFileForTests(const std::string& key,
const base::FilePath& cache_path) {
base::FilePath entry_file_path = cache_path.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
base::PlatformFile entry_file =
base::CreatePlatformFile(entry_file_path, flags, NULL, NULL);
diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc
index 4a89f9b5eb..4291b1f777 100644
--- a/net/disk_cache/simple/simple_util.cc
+++ b/net/disk_cache/simple/simple_util.cc
@@ -77,13 +77,15 @@ uint64 GetEntryHashKey(const std::string& key) {
return u.key_hash;
}
-std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash,
- int index) {
- return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, index);
+std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash,
+ int file_index) {
+ return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, file_index);
}
-std::string GetFilenameFromKeyAndIndex(const std::string& key, int index) {
- return GetEntryHashKeyAsHexString(key) + base::StringPrintf("_%1d", index);
+std::string GetFilenameFromKeyAndFileIndex(const std::string& key,
+ int file_index) {
+ return GetEntryHashKeyAsHexString(key) +
+ base::StringPrintf("_%1d", file_index);
}
int32 GetDataSizeFromKeyAndFileSize(const std::string& key, int64 file_size) {
@@ -98,10 +100,8 @@ int64 GetFileSizeFromKeyAndDataSize(const std::string& key, int32 data_size) {
sizeof(SimpleFileEOF);
}
-int64 GetFileOffsetFromKeyAndDataOffset(const std::string& key,
- int data_offset) {
- const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + key.size();
- return headers_size + data_offset;
+int GetFileIndexFromStreamIndex(int stream_index) {
+ return (stream_index == 2) ? 1 : 0;
}
// TODO(clamy, gavinp): this should go in base
diff --git a/net/disk_cache/simple/simple_util.h b/net/disk_cache/simple/simple_util.h
index ef51869581..60a237ecd8 100644
--- a/net/disk_cache/simple/simple_util.h
+++ b/net/disk_cache/simple/simple_util.h
@@ -40,12 +40,13 @@ NET_EXPORT_PRIVATE bool GetEntryHashKeyFromHexString(
// Given a |key| for a (potential) entry in the simple backend and the |index|
// of a stream on that entry, returns the filename in which that stream would be
// stored.
-NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndIndex(
+NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndFileIndex(
const std::string& key,
- int index);
+ int file_index);
// Same as |GetFilenameFromKeyAndIndex| above, but using a hex string.
-std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash, int index);
+std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash,
+ int file_index);
// Given the size of a file holding a stream in the simple backend and the key
// to an entry, returns the number of bytes in the stream.
@@ -57,11 +58,9 @@ NET_EXPORT_PRIVATE int32 GetDataSizeFromKeyAndFileSize(const std::string& key,
NET_EXPORT_PRIVATE int64 GetFileSizeFromKeyAndDataSize(const std::string& key,
int32 data_size);
-// Given the key to an entry, and an offset into a stream on that entry, returns
-// the file offset corresponding to |data_offset|.
-NET_EXPORT_PRIVATE int64 GetFileOffsetFromKeyAndDataOffset(
- const std::string& key,
- int data_offset);
+// Given the stream index, returns the number of the file the stream is stored
+// in.
+NET_EXPORT_PRIVATE int GetFileIndexFromStreamIndex(int stream_index);
// Fills |out_time| with the time the file last modified time. Unlike the
// functions in platform_file.h, the time resolution is milliseconds.
diff --git a/net/disk_cache/simple/simple_version_upgrade.cc b/net/disk_cache/simple/simple_version_upgrade.cc
new file mode 100644
index 0000000000..dfc6ef4394
--- /dev/null
+++ b/net/disk_cache/simple/simple_version_upgrade.cc
@@ -0,0 +1,203 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/simple/simple_version_upgrade.h"
+
+#include <cstring>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
+#include "net/disk_cache/simple/simple_entry_format_history.h"
+#include "third_party/zlib/zlib.h"
+
+namespace {
+
+// It is not possible to upgrade cache structures on disk that are of version
+// below this, the entire cache should be dropped for them.
+const uint32 kMinVersionAbleToUpgrade = 5;
+
+const char kFakeIndexFileName[] = "index";
+const char kIndexFileName[] = "the-real-index";
+
+void LogMessageFailedUpgradeFromVersion(int version) {
+ LOG(ERROR) << "Failed to upgrade Simple Cache from version: " << version;
+}
+
+bool WriteFakeIndexFile(const base::FilePath& file_name) {
+ base::PlatformFileError error;
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_name,
+ base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
+ NULL,
+ &error);
+ disk_cache::FakeIndexData file_contents;
+ file_contents.initial_magic_number =
+ disk_cache::simplecache_v5::kSimpleInitialMagicNumber;
+ file_contents.version = disk_cache::kSimpleVersion;
+ int bytes_written = base::WritePlatformFile(
+ file, 0, reinterpret_cast<char*>(&file_contents), sizeof(file_contents));
+ if (!base::ClosePlatformFile(file) ||
+ bytes_written != sizeof(file_contents)) {
+ LOG(ERROR) << "Failed to write fake index file: "
+ << file_name.LossyDisplayName();
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace disk_cache {
+
+FakeIndexData::FakeIndexData() {
+ // Make hashing repeatable: leave no padding bytes untouched.
+ std::memset(this, 0, sizeof(*this));
+}
+
+// Migrates the cache directory from version 4 to version 5.
+// Returns true iff it succeeds.
+//
+// The V5 and V6 caches differ in the name of the index file (it moved to a
+// subdirectory) and in the file format (directory last-modified time observed
+// by the index writer has gotten appended to the pickled format).
+//
+// To keep complexity small this specific upgrade code *deletes* the old index
+// file. The directory for the new index file has to be created lazily anyway,
+// so it is not done in the upgrader.
+//
+// Below is the detailed description of index file format differences. It is for
+// reference purposes. This documentation would be useful to move closer to the
+// next index upgrader when the latter gets introduced.
+//
+// Path:
+// V5: $cachedir/the-real-index
+// V6: $cachedir/index-dir/the-real-index
+//
+// Pickled file format:
+// Both formats extend Pickle::Header by 32bit value of the CRC-32 of the
+// pickled data.
+// <v5-index> ::= <v5-index-metadata> <entry-info>*
+// <v5-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
+// UInt32(4)
+// UInt64(<number-of-entries>)
+// UInt64(<cache-size-in-bytes>)
+// <entry-info> ::= UInt64(<hash-of-the-key>)
+// Int64(<entry-last-used-time>)
+// UInt64(<entry-size-in-bytes>)
+// <v6-index> ::= <v6-index-metadata>
+// <entry-info>*
+// Int64(<cache-dir-mtime>)
+// <v6-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
+// UInt32(5)
+// UInt64(<number-of-entries>)
+// UInt64(<cache-size-in-bytes>)
+// Where:
+// <entry-size-in-bytes> is equal the sum of all file sizes of the entry.
+// <cache-dir-mtime> is the last modification time with nanosecond precision
+// of the directory, where all files for entries are stored.
+// <hash-of-the-key> represent the first 64 bits of a SHA-1 of the key.
+bool UpgradeIndexV5V6(const base::FilePath& cache_directory) {
+ const base::FilePath old_index_file =
+ cache_directory.AppendASCII(kIndexFileName);
+ if (!base::DeleteFile(old_index_file, /* recursive = */ false))
+ return false;
+ return true;
+}
+
+// Some points about the Upgrade process are still not clear:
+// 1. if the upgrade path requires dropping cache it would be faster to just
+// return an initialization error here and proceed with asynchronous cache
+// cleanup in CacheCreator. Should this hack be considered valid? Some smart
+// tests may fail.
+// 2. Because Android process management allows for killing a process at any
+// time, the upgrade process may need to deal with a partially completed
+// previous upgrade. For example, while upgrading A -> A + 2 we are the
+// process gets killed and some parts are remaining at version A + 1. There
+// are currently no generic mechanisms to resolve this situation, co the
+// upgrade codes need to ensure they can continue after being stopped in the
+// middle. It also means that the "fake index" must be flushed in between the
+// upgrade steps. Atomicity of this is an interesting research topic. The
+// intermediate fake index flushing must be added as soon as we add more
+// upgrade steps.
+bool UpgradeSimpleCacheOnDisk(const base::FilePath& path) {
+ // There is a convention among disk cache backends: looking at the magic in
+ // the file "index" it should be sufficient to determine if the cache belongs
+ // to the currently running backend. The Simple Backend stores its index in
+ // the file "the-real-index" (see simple_index_file.cc) and the file "index"
+ // only signifies presence of the implementation's magic and version. There
+ // are two reasons for that:
+ // 1. Absence of the index is itself not a fatal error in the Simple Backend
+ // 2. The Simple Backend has pickled file format for the index making it hacky
+ // to have the magic in the right place.
+ const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
+ base::PlatformFileError error;
+ base::PlatformFile fake_index_file = base::CreatePlatformFile(
+ fake_index,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL,
+ &error);
+ if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
+ return WriteFakeIndexFile(fake_index);
+ } else if (error != base::PLATFORM_FILE_OK) {
+ return false;
+ }
+ FakeIndexData file_header;
+ int bytes_read = base::ReadPlatformFile(fake_index_file,
+ 0,
+ reinterpret_cast<char*>(&file_header),
+ sizeof(file_header));
+ if (!base::ClosePlatformFile(fake_index_file) ||
+ bytes_read != sizeof(file_header) ||
+ file_header.initial_magic_number !=
+ disk_cache::simplecache_v5::kSimpleInitialMagicNumber) {
+ LOG(ERROR) << "File structure does not match the disk cache backend.";
+ return false;
+ }
+
+ uint32 version_from = file_header.version;
+ if (version_from < kMinVersionAbleToUpgrade ||
+ version_from > kSimpleVersion) {
+ LOG(ERROR) << "Inconsistent cache version.";
+ return false;
+ }
+ bool upgrade_needed = (version_from != kSimpleVersion);
+ if (version_from == kMinVersionAbleToUpgrade) {
+ // Upgrade only the index for V4 -> V5 move.
+ if (!UpgradeIndexV5V6(path)) {
+ LogMessageFailedUpgradeFromVersion(file_header.version);
+ return false;
+ }
+ version_from++;
+ }
+ if (version_from == kSimpleVersion) {
+ if (!upgrade_needed) {
+ return true;
+ } else {
+ const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index");
+ if (!WriteFakeIndexFile(temp_fake_index)) {
+ base::DeleteFile(temp_fake_index, /* recursive = */ false);
+ LOG(ERROR) << "Failed to write a new fake index.";
+ LogMessageFailedUpgradeFromVersion(file_header.version);
+ return false;
+ }
+ if (!base::ReplaceFile(temp_fake_index, fake_index, NULL)) {
+ LOG(ERROR) << "Failed to replace the fake index.";
+ LogMessageFailedUpgradeFromVersion(file_header.version);
+ return false;
+ }
+ return true;
+ }
+ }
+ // Verify during the test stage that the upgraders are implemented for all
+ // versions. The release build would cause backend initialization failure
+ // which would then later lead to removing all files known to the backend.
+ DCHECK_EQ(kSimpleVersion, version_from);
+ return false;
+}
+
+} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_version_upgrade.h b/net/disk_cache/simple/simple_version_upgrade.h
new file mode 100644
index 0000000000..352379b997
--- /dev/null
+++ b/net/disk_cache/simple/simple_version_upgrade.h
@@ -0,0 +1,50 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_VERSION_UPGRADE_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_VERSION_UPGRADE_H_
+
+// Defines functionality to upgrade the file structure of the Simple Cache
+// Backend on disk. Assumes no backend operations are running simultaneously.
+// Hence must be run at cache initialization step.
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace disk_cache {
+
+// Performs all necessary disk IO to upgrade the cache structure if it is
+// needed.
+//
+// Returns true iff no errors were found during consistency checks and all
+// necessary transitions succeeded. If this function fails, there is nothing
+// left to do other than dropping the whole cache directory.
+NET_EXPORT_PRIVATE bool UpgradeSimpleCacheOnDisk(const base::FilePath& path);
+
+// The format for the fake index has mistakenly acquired two extra fields that
+// do not contain any useful data. Since they were equal to zero, they are now
+// mandatated to be zero.
+struct NET_EXPORT_PRIVATE FakeIndexData {
+ FakeIndexData();
+
+ // Must be equal to simplecache_v4::kSimpleInitialMagicNumber.
+ uint64 initial_magic_number;
+
+ // Must be equal kSimpleVersion when the cache backend is instantiated.
+ uint32 version;
+
+ uint32 unused_must_be_zero1;
+ uint32 unused_must_be_zero2;
+};
+
+// Exposed for testing.
+NET_EXPORT_PRIVATE bool UpgradeIndexV5V6(const base::FilePath& cache_directory);
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_VERSION_UPGRADE_H_
diff --git a/net/disk_cache/simple/simple_version_upgrade_unittest.cc b/net/disk_cache/simple/simple_version_upgrade_unittest.cc
new file mode 100644
index 0000000000..c9d42f1066
--- /dev/null
+++ b/net/disk_cache/simple/simple_version_upgrade_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/simple/simple_version_upgrade.h"
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
+#include "net/disk_cache/simple/simple_entry_format_history.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The migration process relies on ability to rename newly created files, which
+// could be problematic on Windows XP.
+#if defined(OS_POSIX)
+
+namespace {
+
+// Same as |disk_cache::kSimpleInitialMagicNumber|.
+const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30);
+
+// The "fake index" file that cache backends use to distinguish whether the
+// cache belongs to one backend or another.
+const char kFakeIndexFileName[] = "index";
+
+// Same as |SimpleIndexFile::kIndexFileName|.
+const char kIndexFileName[] = "the-real-index";
+
+// Same as |SimpleIndexFile::kIndexDirectory|.
+const char kIndexDirectory[] = "index-dir";
+
+// Same as |SimpleIndexFile::kTempIndexFileName|.
+const char kTempIndexFileName[] = "temp-index";
+
+bool WriteFakeIndexFileV5(const base::FilePath& cache_path) {
+ disk_cache::FakeIndexData data;
+ data.version = 5;
+ data.initial_magic_number = kSimpleInitialMagicNumber;
+ data.unused_must_be_zero1 = 0;
+ data.unused_must_be_zero2 = 0;
+ const base::FilePath file_name = cache_path.AppendASCII("index");
+ return sizeof(data) ==
+ file_util::WriteFile(
+ file_name, reinterpret_cast<const char*>(&data), sizeof(data));
+}
+
+TEST(SimpleVersionUpgradeTest, FailsToMigrateBackwards) {
+ base::ScopedTempDir cache_dir;
+ ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
+ const base::FilePath cache_path = cache_dir.path();
+
+ disk_cache::FakeIndexData data;
+ data.version = 100500;
+ data.initial_magic_number = kSimpleInitialMagicNumber;
+ data.unused_must_be_zero1 = 0;
+ data.unused_must_be_zero2 = 0;
+ const base::FilePath file_name = cache_path.AppendASCII(kFakeIndexFileName);
+ ASSERT_EQ(implicit_cast<int>(sizeof(data)),
+ file_util::WriteFile(
+ file_name, reinterpret_cast<const char*>(&data), sizeof(data)));
+ EXPECT_FALSE(disk_cache::UpgradeSimpleCacheOnDisk(cache_dir.path()));
+}
+
+TEST(SimpleVersionUpgradeTest, FakeIndexVersionGetsUpdated) {
+ base::ScopedTempDir cache_dir;
+ ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
+ const base::FilePath cache_path = cache_dir.path();
+
+ WriteFakeIndexFileV5(cache_path);
+ const std::string file_contents("incorrectly serialized data");
+ const base::FilePath index_file = cache_path.AppendASCII(kIndexFileName);
+ ASSERT_EQ(implicit_cast<int>(file_contents.size()),
+ file_util::WriteFile(
+ index_file, file_contents.data(), file_contents.size()));
+
+ // Upgrade.
+ ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path));
+
+ // Check that the version in the fake index file is updated.
+ std::string new_fake_index_contents;
+ ASSERT_TRUE(base::ReadFileToString(cache_path.AppendASCII(kFakeIndexFileName),
+ &new_fake_index_contents));
+ const disk_cache::FakeIndexData* fake_index_header;
+ EXPECT_EQ(sizeof(*fake_index_header), new_fake_index_contents.size());
+ fake_index_header = reinterpret_cast<const disk_cache::FakeIndexData*>(
+ new_fake_index_contents.data());
+ EXPECT_EQ(disk_cache::kSimpleVersion, fake_index_header->version);
+ EXPECT_EQ(kSimpleInitialMagicNumber, fake_index_header->initial_magic_number);
+}
+
+TEST(SimpleVersionUpgradeTest, UpgradeV5V6IndexMustDisappear) {
+ base::ScopedTempDir cache_dir;
+ ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
+ const base::FilePath cache_path = cache_dir.path();
+
+ WriteFakeIndexFileV5(cache_path);
+ const std::string file_contents("incorrectly serialized data");
+ const base::FilePath index_file = cache_path.AppendASCII(kIndexFileName);
+ ASSERT_EQ(implicit_cast<int>(file_contents.size()),
+ file_util::WriteFile(
+ index_file, file_contents.data(), file_contents.size()));
+
+ // Create a few entry-like files.
+ const uint64 kEntries = 5;
+ for (uint64 entry_hash = 0; entry_hash < kEntries; ++entry_hash) {
+ for (int index = 0; index < 3; ++index) {
+ std::string file_name =
+ base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, index);
+ std::string entry_contents =
+ file_contents +
+ base::StringPrintf(" %" PRIx64, implicit_cast<uint64>(entry_hash));
+ ASSERT_EQ(implicit_cast<int>(entry_contents.size()),
+ file_util::WriteFile(cache_path.AppendASCII(file_name),
+ entry_contents.data(),
+ entry_contents.size()));
+ }
+ }
+
+ // Upgrade.
+ ASSERT_TRUE(disk_cache::UpgradeIndexV5V6(cache_path));
+
+ // Check that the old index disappeared but the files remain unchanged.
+ EXPECT_FALSE(base::PathExists(index_file));
+ for (uint64 entry_hash = 0; entry_hash < kEntries; ++entry_hash) {
+ for (int index = 0; index < 3; ++index) {
+ std::string file_name =
+ base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, index);
+ std::string expected_contents =
+ file_contents +
+ base::StringPrintf(" %" PRIx64, implicit_cast<uint64>(entry_hash));
+ std::string real_contents;
+ EXPECT_TRUE(base::ReadFileToString(cache_path.AppendASCII(file_name),
+ &real_contents));
+ EXPECT_EQ(expected_contents, real_contents);
+ }
+ }
+}
+
+} // namespace
+
+#endif // defined(OS_POSIX)
diff --git a/net/disk_cache/v3/block_bitmaps.cc b/net/disk_cache/v3/block_bitmaps.cc
index 0d0317b39d..b68ecdd14f 100644
--- a/net/disk_cache/v3/block_bitmaps.cc
+++ b/net/disk_cache/v3/block_bitmaps.cc
@@ -2,143 +2,73 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/disk_cache/block_files.h"
+#include "net/disk_cache/v3/block_bitmaps.h"
-#include "base/atomicops.h"
-#include "base/file_util.h"
#include "base/metrics/histogram.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_checker.h"
#include "base/time/time.h"
-#include "net/disk_cache/cache_util.h"
-#include "net/disk_cache/file_lock.h"
+#include "net/disk_cache/disk_format_base.h"
#include "net/disk_cache/trace.h"
using base::TimeTicks;
namespace disk_cache {
-BlockFiles::BlockFiles(const base::FilePath& path)
- : init_(false), zero_buffer_(NULL), path_(path) {
+BlockBitmaps::BlockBitmaps() {
}
-BlockFiles::~BlockFiles() {
- if (zero_buffer_)
- delete[] zero_buffer_;
- CloseFiles();
+BlockBitmaps::~BlockBitmaps() {
}
-bool BlockFiles::Init(bool create_files) {
- DCHECK(!init_);
- if (init_)
- return false;
-
- thread_checker_.reset(new base::ThreadChecker);
-
- block_files_.resize(kFirstAdditionalBlockFile);
- for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
- if (create_files)
- if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true))
- return false;
-
- if (!OpenBlockFile(i))
- return false;
-
- // Walk this chain of files removing empty ones.
- if (!RemoveEmptyFile(static_cast<FileType>(i + 1)))
- return false;
- }
-
- init_ = true;
- return true;
+void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) {
+ bitmaps_ = bitmaps;
}
-bool BlockFiles::CreateBlock(FileType block_type, int block_count,
- Addr* block_address) {
- DCHECK(thread_checker_->CalledOnValidThread());
- if (block_type < RANKINGS || block_type > BLOCK_4K ||
- block_count < 1 || block_count > 4)
- return false;
- if (!init_)
+bool BlockBitmaps::CreateBlock(FileType block_type,
+ int block_count,
+ Addr* block_address) {
+ DCHECK_NE(block_type, EXTERNAL);
+ DCHECK_NE(block_type, RANKINGS);
+ if (block_count < 1 || block_count > kMaxNumBlocks)
return false;
- MappedFile* file = FileForNewBlock(block_type, block_count);
- if (!file)
+ int header_num = HeaderNumberForNewBlock(block_type, block_count);
+ if (header_num < 0)
return false;
- ScopedFlush flush(file);
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
-
- int target_size = 0;
- for (int i = block_count; i <= 4; i++) {
- if (header->empty[i - 1]) {
- target_size = i;
- break;
- }
- }
-
- DCHECK(target_size);
int index;
- if (!CreateMapBlock(target_size, block_count, header, &index))
+ if (!bitmaps_[header_num].CreateMapBlock(block_count, &index))
+ return false;
+
+ if (!index && (block_type == BLOCK_ENTRIES || block_type == BLOCK_EVICTED) &&
+ !bitmaps_[header_num].CreateMapBlock(block_count, &index)) {
+ // index 0 for entries is a reserved value.
return false;
+ }
- Addr address(block_type, block_count, header->this_file, index);
+ Addr address(block_type, block_count, bitmaps_[header_num].FileId(), index);
block_address->set_value(address.value());
Trace("CreateBlock 0x%x", address.value());
return true;
}
-void BlockFiles::DeleteBlock(Addr address, bool deep) {
- DCHECK(thread_checker_->CalledOnValidThread());
+void BlockBitmaps::DeleteBlock(Addr address) {
if (!address.is_initialized() || address.is_separate_file())
return;
- if (!zero_buffer_) {
- zero_buffer_ = new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4];
- memset(zero_buffer_, 0, Addr::BlockSizeForFileType(BLOCK_4K) * 4);
- }
- MappedFile* file = GetFile(address);
- if (!file)
+ int header_num = GetHeaderNumber(address);
+ if (header_num < 0)
return;
Trace("DeleteBlock 0x%x", address.value());
-
- size_t size = address.BlockSize() * address.num_blocks();
- size_t offset = address.start_block() * address.BlockSize() +
- kBlockHeaderSize;
- if (deep)
- file->Write(zero_buffer_, size, offset);
-
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- DeleteMapBlock(address.start_block(), address.num_blocks(), header);
- file->Flush();
-
- if (!header->num_entries) {
- // This file is now empty. Let's try to delete it.
- FileType type = Addr::RequiredFileType(header->entry_size);
- if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size)
- type = RANKINGS;
- RemoveEmptyFile(type); // Ignore failures.
- }
+ bitmaps_[header_num].DeleteMapBlock(address.start_block(),
+ address.num_blocks());
}
-void BlockFiles::CloseFiles() {
- if (init_) {
- DCHECK(thread_checker_->CalledOnValidThread());
- }
- init_ = false;
- for (unsigned int i = 0; i < block_files_.size(); i++) {
- if (block_files_[i]) {
- block_files_[i]->Release();
- block_files_[i] = NULL;
- }
- }
- block_files_.clear();
+void BlockBitmaps::Clear() {
+ bitmaps_.clear();
}
-void BlockFiles::ReportStats() {
- DCHECK(thread_checker_->CalledOnValidThread());
+void BlockBitmaps::ReportStats() {
int used_blocks[kFirstAdditionalBlockFile];
int load[kFirstAdditionalBlockFile];
for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
@@ -155,176 +85,92 @@ void BlockFiles::ReportStats() {
UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101);
}
-bool BlockFiles::IsValid(Addr address) {
+bool BlockBitmaps::IsValid(Addr address) {
#ifdef NDEBUG
return true;
#else
if (!address.is_initialized() || address.is_separate_file())
return false;
- MappedFile* file = GetFile(address);
- if (!file)
+ int header_num = GetHeaderNumber(address);
+ if (header_num < 0)
return false;
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- bool rv = UsedMapBlock(address.start_block(), address.num_blocks(), header);
+ bool rv = bitmaps_[header_num].UsedMapBlock(address.start_block(),
+ address.num_blocks());
DCHECK(rv);
-
- static bool read_contents = false;
- if (read_contents) {
- scoped_ptr<char[]> buffer;
- buffer.reset(new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4]);
- size_t size = address.BlockSize() * address.num_blocks();
- size_t offset = address.start_block() * address.BlockSize() +
- kBlockHeaderSize;
- bool ok = file->Read(buffer.get(), size, offset);
- DCHECK(ok);
- }
-
return rv;
#endif
}
-MappedFile* BlockFiles::GetFile(Addr address) {
- DCHECK(thread_checker_->CalledOnValidThread());
- DCHECK(block_files_.size() >= 4);
+int BlockBitmaps::GetHeaderNumber(Addr address) {
+ DCHECK_GE(bitmaps_.size(), static_cast<size_t>(kFirstAdditionalBlockFileV3));
DCHECK(address.is_block_file() || !address.is_initialized());
if (!address.is_initialized())
- return NULL;
+ return -1;
int file_index = address.FileNumber();
- if (static_cast<unsigned int>(file_index) >= block_files_.size() ||
- !block_files_[file_index]) {
- // We need to open the file
- if (!OpenBlockFile(file_index))
- return NULL;
- }
- DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
- return block_files_[file_index];
-}
-
-bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) {
- if (kMaxBlocks == header->max_entries)
- return false;
-
- ScopedFlush flush(file);
- DCHECK(!header->empty[3]);
- int new_size = header->max_entries + 1024;
- if (new_size > kMaxBlocks)
- new_size = kMaxBlocks;
-
- int new_size_bytes = new_size * header->entry_size + sizeof(*header);
-
- if (!file->SetLength(new_size_bytes)) {
- // Most likely we are trying to truncate the file, so the header is wrong.
- if (header->updating < 10 && !FixBlockFileHeader(file)) {
- // If we can't fix the file increase the lock guard so we'll pick it on
- // the next start and replace it.
- header->updating = 100;
- return false;
- }
- return (header->max_entries >= new_size);
- }
+ if (static_cast<unsigned int>(file_index) >= bitmaps_.size())
+ return -1;
- FileLock lock(header);
- header->empty[3] = (new_size - header->max_entries) / 4; // 4 blocks entries
- header->max_entries = new_size;
-
- return true;
+ return file_index;
}
-MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) {
- COMPILE_ASSERT(RANKINGS == 1, invalid_file_type);
- MappedFile* file = block_files_[block_type - 1];
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
+int BlockBitmaps::HeaderNumberForNewBlock(FileType block_type,
+ int block_count) {
+ DCHECK_GT(block_type, 0);
+ int header_num = block_type - 1;
+ bool found = true;
TimeTicks start = TimeTicks::Now();
- while (NeedToGrowBlockFile(header, block_count)) {
- if (kMaxBlocks == header->max_entries) {
- file = NextFile(file);
- if (!file)
- return NULL;
- header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- continue;
+ while (bitmaps_[header_num].NeedToGrowBlockFile(block_count)) {
+ header_num = bitmaps_[header_num].NextFileId();
+ if (!header_num) {
+ found = false;
+ break;
}
-
- if (!GrowBlockFile(file, header))
- return NULL;
- break;
}
- HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
- return file;
-}
-// Note that we expect to be called outside of a FileLock... however, we cannot
-// DCHECK on header->updating because we may be fixing a crash.
-bool BlockFiles::FixBlockFileHeader(MappedFile* file) {
- ScopedFlush flush(file);
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- int file_size = static_cast<int>(file->GetLength());
- if (file_size < static_cast<int>(sizeof(*header)))
- return false; // file_size > 2GB is also an error.
-
- const int kMinBlockSize = 36;
- const int kMaxBlockSize = 4096;
- if (header->entry_size < kMinBlockSize ||
- header->entry_size > kMaxBlockSize || header->num_entries < 0)
- return false;
-
- // Make sure that we survive crashes.
- header->updating = 1;
- int expected = header->entry_size * header->max_entries + sizeof(*header);
- if (file_size != expected) {
- int max_expected = header->entry_size * kMaxBlocks + sizeof(*header);
- if (file_size < expected || header->empty[3] || file_size > max_expected) {
- NOTREACHED();
- LOG(ERROR) << "Unexpected file size";
- return false;
- }
- // We were in the middle of growing the file.
- int num_entries = (file_size - sizeof(*header)) / header->entry_size;
- header->max_entries = num_entries;
+ if (!found) {
+ // Restart the search, looking for any file with space. We know that all
+ // files of this type are low on free blocks, but we cannot grow any file
+ // at this time.
+ header_num = block_type - 1;
+ do {
+ if (bitmaps_[header_num].CanAllocate(block_count)) {
+ found = true; // Make sure file 0 is not mistaken with a failure.
+ break;
+ }
+ header_num = bitmaps_[header_num].NextFileId();
+ } while (header_num);
+
+ if (!found)
+ header_num = -1;
}
- FixAllocationCounters(header);
- int empty_blocks = EmptyBlocks(header);
- if (empty_blocks + header->num_entries > header->max_entries)
- header->num_entries = header->max_entries - empty_blocks;
-
- if (!ValidateCounters(header))
- return false;
-
- header->updating = 0;
- return true;
+ HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
+ return header_num;
}
// We are interested in the total number of blocks used by this file type, and
// the max number of blocks that we can store (reported as the percentage of
// used blocks). In order to find out the number of used blocks, we have to
// substract the empty blocks from the total blocks for each file in the chain.
-void BlockFiles::GetFileStats(int index, int* used_count, int* load) {
+void BlockBitmaps::GetFileStats(int index, int* used_count, int* load) {
int max_blocks = 0;
*used_count = 0;
*load = 0;
- for (;;) {
- if (!block_files_[index] && !OpenBlockFile(index))
- return;
-
- BlockFileHeader* header =
- reinterpret_cast<BlockFileHeader*>(block_files_[index]->buffer());
-
- max_blocks += header->max_entries;
- int used = header->max_entries;
- for (int i = 0; i < 4; i++) {
- used -= header->empty[i] * (i + 1);
- DCHECK_GE(used, 0);
- }
+ do {
+ int capacity = bitmaps_[index].Capacity();
+ int used = capacity - bitmaps_[index].EmptyBlocks();
+ DCHECK_GE(used, 0);
+
+ max_blocks += capacity;
*used_count += used;
- if (!header->next_file)
- break;
- index = header->next_file;
- }
+ index = bitmaps_[index].NextFileId();
+ } while (index);
+
if (max_blocks)
*load = *used_count * 100 / max_blocks;
}
diff --git a/net/disk_cache/v3/block_bitmaps.h b/net/disk_cache/v3/block_bitmaps.h
index eaf8760991..111d57b0b1 100644
--- a/net/disk_cache/v3/block_bitmaps.h
+++ b/net/disk_cache/v3/block_bitmaps.h
@@ -4,43 +4,39 @@
// See net/disk_cache/disk_cache.h for the public interface.
-#ifndef NET_DISK_CACHE_BLOCK_FILES_H_
-#define NET_DISK_CACHE_BLOCK_FILES_H_
-
-#include <vector>
+#ifndef NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
+#define NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
#include "net/base/net_export.h"
#include "net/disk_cache/addr.h"
-#include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/block_files.h"
namespace disk_cache {
-// This class handles the set of block-files open by the disk cache.
-class NET_EXPORT_PRIVATE BlockFiles {
+class BackendImplV3;
+
+// This class is the interface in the v3 disk cache to the set of files holding
+// cached data that is small enough to not be efficiently stored in a dedicated
+// file (i.e. < kMaxBlockSize). It is primarily used to allocate and free
+// regions in those files used to store data.
+class NET_EXPORT_PRIVATE BlockBitmaps {
public:
- explicit BlockFiles(const base::FilePath& path);
- ~BlockFiles();
+ BlockBitmaps();
+ ~BlockBitmaps();
- // Performs the object initialization. create_files indicates if the backing
- // files should be created or just open.
- bool Init(bool create_files);
+ void Init(const BlockFilesBitmaps& bitmaps);
// Creates a new entry on a block file. block_type indicates the size of block
// to be used (as defined on cache_addr.h), block_count is the number of
// blocks to allocate, and block_address is the address of the new entry.
bool CreateBlock(FileType block_type, int block_count, Addr* block_address);
- // Removes an entry from the block files. If deep is true, the storage is zero
- // filled; otherwise the entry is removed but the data is not altered (must be
- // already zeroed).
- void DeleteBlock(Addr address, bool deep);
+ // Removes an entry from the block files.
+ void DeleteBlock(Addr address);
- // Close all the files and set the internal state to be initializad again. The
- // cache is being purged.
- void CloseFiles();
+ // Releases the internal bitmaps. The cache is being purged.
+ void Clear();
// Sends UMA stats.
void ReportStats();
@@ -50,26 +46,20 @@ class NET_EXPORT_PRIVATE BlockFiles {
bool IsValid(Addr address);
private:
- // Returns the file that stores a given address.
- MappedFile* GetFile(Addr address);
-
- // Attemp to grow this file. Fails if the file cannot be extended anymore.
- bool GrowBlockFile(MappedFile* file, BlockFileHeader* header);
-
- // Returns the appropriate file to use for a new block.
- MappedFile* FileForNewBlock(FileType block_type, int block_count);
+ // Returns the header number that stores a given address.
+ int GetHeaderNumber(Addr address);
- // Restores the header of a potentially inconsistent file.
- bool FixBlockFileHeader(MappedFile* file);
+ // Returns the appropriate header to use for a new block.
+ int HeaderNumberForNewBlock(FileType block_type, int block_count);
// Retrieves stats for the given file index.
void GetFileStats(int index, int* used_count, int* load);
- bool init_;
+ BlockFilesBitmaps bitmaps_;
- DISALLOW_COPY_AND_ASSIGN(BlockFiles);
+ DISALLOW_COPY_AND_ASSIGN(BlockBitmaps);
};
} // namespace disk_cache
-#endif // NET_DISK_CACHE_BLOCK_FILES_H_
+#endif // NET_DISK_CACHE_V3_BLOCK_BITMAPS_H_
diff --git a/net/disk_cache/v3/block_bitmaps_unittest.cc b/net/disk_cache/v3/block_bitmaps_unittest.cc
index fa7c5dbb74..981bdecfce 100644
--- a/net/disk_cache/v3/block_bitmaps_unittest.cc
+++ b/net/disk_cache/v3/block_bitmaps_unittest.cc
@@ -2,342 +2,64 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/file_util.h"
-#include "base/files/file_enumerator.h"
+#include "net/disk_cache/addr.h"
#include "net/disk_cache/block_files.h"
-#include "net/disk_cache/disk_cache.h"
-#include "net/disk_cache/disk_cache_test_base.h"
-#include "net/disk_cache/disk_cache_test_util.h"
+#include "net/disk_cache/disk_format_base.h"
+#include "net/disk_cache/v3/block_bitmaps.h"
#include "testing/gtest/include/gtest/gtest.h"
-using base::Time;
-
-namespace {
-
-// Returns the number of files in this folder.
-int NumberOfFiles(const base::FilePath& path) {
- base::FileEnumerator iter(path, false, base::FileEnumerator::FILES);
- int count = 0;
- for (base::FilePath file = iter.Next(); !file.value().empty();
- file = iter.Next()) {
- count++;
- }
- return count;
-}
-
-} // namespace;
-
-namespace disk_cache {
-
-TEST_F(DiskCacheTest, BlockFiles_Grow) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- const int kMaxSize = 35000;
- Addr address[kMaxSize];
-
- // Fill up the 32-byte block file (use three files).
- for (int i = 0; i < kMaxSize; i++) {
- EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &address[i]));
- }
- EXPECT_EQ(6, NumberOfFiles(cache_path_));
-
- // Make sure we don't keep adding files.
- for (int i = 0; i < kMaxSize * 4; i += 2) {
- int target = i % kMaxSize;
- files.DeleteBlock(address[target], false);
- EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &address[target]));
- }
- EXPECT_EQ(6, NumberOfFiles(cache_path_));
-}
-
-// We should be able to delete empty block files.
-TEST_F(DiskCacheTest, BlockFiles_Shrink) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- const int kMaxSize = 35000;
- Addr address[kMaxSize];
-
- // Fill up the 32-byte block file (use three files).
- for (int i = 0; i < kMaxSize; i++) {
- EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &address[i]));
- }
-
- // Now delete all the blocks, so that we can delete the two extra files.
- for (int i = 0; i < kMaxSize; i++) {
- files.DeleteBlock(address[i], false);
- }
- EXPECT_EQ(4, NumberOfFiles(cache_path_));
-}
-
-// Handling of block files not properly closed.
-TEST_F(DiskCacheTest, BlockFiles_Recover) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- const int kNumEntries = 2000;
- CacheAddr entries[kNumEntries];
-
- int seed = static_cast<int>(Time::Now().ToInternalValue());
- srand(seed);
- for (int i = 0; i < kNumEntries; i++) {
- Addr address(0);
- int size = (rand() % 4) + 1;
- EXPECT_TRUE(files.CreateBlock(RANKINGS, size, &address));
- entries[i] = address.value();
- }
-
- for (int i = 0; i < kNumEntries; i++) {
- int source1 = rand() % kNumEntries;
- int source2 = rand() % kNumEntries;
- CacheAddr temp = entries[source1];
- entries[source1] = entries[source2];
- entries[source2] = temp;
- }
-
- for (int i = 0; i < kNumEntries / 2; i++) {
- Addr address(entries[i]);
- files.DeleteBlock(address, false);
- }
-
- // At this point, there are kNumEntries / 2 entries on the file, randomly
- // distributed both on location and size.
-
- Addr address(entries[kNumEntries / 2]);
- MappedFile* file = files.GetFile(address);
- ASSERT_TRUE(NULL != file);
-
- BlockFileHeader* header =
- reinterpret_cast<BlockFileHeader*>(file->buffer());
- ASSERT_TRUE(NULL != header);
-
- ASSERT_EQ(0, header->updating);
-
- int max_entries = header->max_entries;
- int empty_1 = header->empty[0];
- int empty_2 = header->empty[1];
- int empty_3 = header->empty[2];
- int empty_4 = header->empty[3];
-
- // Corrupt the file.
- header->max_entries = header->empty[0] = 0;
- header->empty[1] = header->empty[2] = header->empty[3] = 0;
- header->updating = -1;
-
- files.CloseFiles();
-
- ASSERT_TRUE(files.Init(false));
-
- // The file must have been fixed.
- file = files.GetFile(address);
- ASSERT_TRUE(NULL != file);
-
- header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- ASSERT_TRUE(NULL != header);
-
- ASSERT_EQ(0, header->updating);
-
- EXPECT_EQ(max_entries, header->max_entries);
- EXPECT_EQ(empty_1, header->empty[0]);
- EXPECT_EQ(empty_2, header->empty[1]);
- EXPECT_EQ(empty_3, header->empty[2]);
- EXPECT_EQ(empty_4, header->empty[3]);
-}
-
-// Handling of truncated files.
-TEST_F(DiskCacheTest, BlockFiles_ZeroSizeFile) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- base::FilePath filename = files.Name(0);
- files.CloseFiles();
- // Truncate one of the files.
- {
- scoped_refptr<File> file(new File);
- ASSERT_TRUE(file->Init(filename));
- EXPECT_TRUE(file->SetLength(0));
- }
-
- // Initializing should fail, not crash.
- ASSERT_FALSE(files.Init(false));
-}
-
-// Handling of truncated files (non empty).
-TEST_F(DiskCacheTest, BlockFiles_TruncatedFile) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
- Addr address;
- EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
-
- base::FilePath filename = files.Name(0);
- files.CloseFiles();
- // Truncate one of the files.
- {
- scoped_refptr<File> file(new File);
- ASSERT_TRUE(file->Init(filename));
- EXPECT_TRUE(file->SetLength(15000));
- }
-
- // Initializing should fail, not crash.
- ASSERT_FALSE(files.Init(false));
-}
-
-// Tests detection of out of sync counters.
-TEST_F(DiskCacheTest, BlockFiles_Counters) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- // Create a block of size 2.
- Addr address(0);
- EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
-
- MappedFile* file = files.GetFile(address);
- ASSERT_TRUE(NULL != file);
-
- BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- ASSERT_TRUE(NULL != header);
- ASSERT_EQ(0, header->updating);
-
- // Alter the counters so that the free space doesn't add up.
- header->empty[2] = 50; // 50 free blocks of size 3.
- files.CloseFiles();
-
- ASSERT_TRUE(files.Init(false));
- file = files.GetFile(address);
- ASSERT_TRUE(NULL != file);
- header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- ASSERT_TRUE(NULL != header);
-
- // The file must have been fixed.
- ASSERT_EQ(0, header->empty[2]);
-
- // Change the number of entries.
- header->num_entries = 3;
- header->updating = 1;
- files.CloseFiles();
-
- ASSERT_TRUE(files.Init(false));
- file = files.GetFile(address);
- ASSERT_TRUE(NULL != file);
- header = reinterpret_cast<BlockFileHeader*>(file->buffer());
- ASSERT_TRUE(NULL != header);
-
- // The file must have been "fixed".
- ASSERT_EQ(2, header->num_entries);
-
- // Change the number of entries.
- header->num_entries = -1;
- header->updating = 1;
- files.CloseFiles();
-
- // Detect the error.
- ASSERT_FALSE(files.Init(false));
-}
-
-// An invalid file can be detected after init.
-TEST_F(DiskCacheTest, BlockFiles_InvalidFile) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
-
- // Let's access block 10 of file 5. (There is no file).
- Addr addr(BLOCK_256, 1, 5, 10);
- EXPECT_TRUE(NULL == files.GetFile(addr));
-
- // Let's create an invalid file.
- base::FilePath filename(files.Name(5));
- char header[kBlockHeaderSize];
- memset(header, 'a', kBlockHeaderSize);
- EXPECT_EQ(kBlockHeaderSize,
- file_util::WriteFile(filename, header, kBlockHeaderSize));
-
- EXPECT_TRUE(NULL == files.GetFile(addr));
-
- // The file should not have been changed (it is still invalid).
- EXPECT_TRUE(NULL == files.GetFile(addr));
-}
-
-// Tests that we generate the correct file stats.
-TEST_F(DiskCacheTest, BlockFiles_Stats) {
- ASSERT_TRUE(CopyTestCache("remove_load1"));
-
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(false));
- int used, load;
-
- files.GetFileStats(0, &used, &load);
- EXPECT_EQ(101, used);
- EXPECT_EQ(9, load);
-
- files.GetFileStats(1, &used, &load);
- EXPECT_EQ(203, used);
- EXPECT_EQ(19, load);
-
- files.GetFileStats(2, &used, &load);
- EXPECT_EQ(0, used);
- EXPECT_EQ(0, load);
-}
-
// Tests that we add and remove blocks correctly.
-TEST_F(DiskCacheTest, AllocationMap) {
- ASSERT_TRUE(CleanupCacheDir());
- ASSERT_TRUE(file_util::CreateDirectory(cache_path_));
+TEST(DiskCacheBlockBitmaps, V3AllocationMap) {
+ disk_cache::BlockBitmaps block_bitmaps;
+ disk_cache::BlockFilesBitmaps bitmaps;
+
+ const int kNumHeaders = 10;
+ disk_cache::BlockFileHeader headers[kNumHeaders];
+ for (int i = 0; i < kNumHeaders; i++) {
+ memset(&headers[i], 0, sizeof(headers[i]));
+ headers[i].magic = disk_cache::kBlockMagic;
+ headers[i].version = disk_cache::kBlockCurrentVersion;
+ headers[i].this_file = static_cast<int16>(i);
+ headers[i].empty[3] = 200;
+ headers[i].max_entries = 800;
+ bitmaps.push_back(disk_cache::BlockHeader(&headers[i]));
+ }
- BlockFiles files(cache_path_);
- ASSERT_TRUE(files.Init(true));
+ block_bitmaps.Init(bitmaps);
// Create a bunch of entries.
const int kSize = 100;
- Addr address[kSize];
+ disk_cache::Addr address[kSize];
for (int i = 0; i < kSize; i++) {
SCOPED_TRACE(i);
int block_size = i % 4 + 1;
- EXPECT_TRUE(files.CreateBlock(BLOCK_1K, block_size, &address[i]));
- EXPECT_EQ(BLOCK_1K, address[i].file_type());
+ ASSERT_TRUE(block_bitmaps.CreateBlock(disk_cache::BLOCK_1K, block_size,
+ &address[i]));
+ EXPECT_EQ(disk_cache::BLOCK_1K, address[i].file_type());
EXPECT_EQ(block_size, address[i].num_blocks());
int start = address[i].start_block();
+
+ // Verify that the allocated entry doesn't cross a 4 block boundary.
EXPECT_EQ(start / 4, (start + block_size - 1) / 4);
}
for (int i = 0; i < kSize; i++) {
SCOPED_TRACE(i);
- EXPECT_TRUE(files.IsValid(address[i]));
+ EXPECT_TRUE(block_bitmaps.IsValid(address[i]));
}
// The first part of the allocation map should be completely filled. We used
- // 10 bits per each four entries, so 250 bits total.
- BlockFileHeader* header =
- reinterpret_cast<BlockFileHeader*>(files.GetFile(address[0])->buffer());
- uint8* buffer = reinterpret_cast<uint8*>(&header->allocation_map);
- for (int i =0; i < 29; i++) {
+ // 10 bits per each of four entries, so 250 bits total. All entries should go
+ // to the third file.
+ uint8* buffer = reinterpret_cast<uint8*>(&headers[2].allocation_map);
+ for (int i = 0; i < 29; i++) {
SCOPED_TRACE(i);
EXPECT_EQ(0xff, buffer[i]);
}
for (int i = 0; i < kSize; i++) {
SCOPED_TRACE(i);
- files.DeleteBlock(address[i], false);
+ block_bitmaps.DeleteBlock(address[i]);
}
// The allocation map should be empty.
@@ -346,5 +68,3 @@ TEST_F(DiskCacheTest, AllocationMap) {
EXPECT_EQ(0, buffer[i]);
}
}
-
-} // namespace disk_cache
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index d7af988cf8..6613182557 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -21,7 +21,8 @@ DnsConfig::DnsConfig()
timeout(base::TimeDelta::FromSeconds(kDnsTimeoutSeconds)),
attempts(2),
rotate(false),
- edns0(false) {}
+ edns0(false),
+ use_local_ipv6(false) {}
DnsConfig::~DnsConfig() {}
@@ -38,7 +39,8 @@ bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const {
(timeout == d.timeout) &&
(attempts == d.attempts) &&
(rotate == d.rotate) &&
- (edns0 == d.edns0);
+ (edns0 == d.edns0) &&
+ (use_local_ipv6 == d.use_local_ipv6);
}
void DnsConfig::CopyIgnoreHosts(const DnsConfig& d) {
@@ -51,6 +53,7 @@ void DnsConfig::CopyIgnoreHosts(const DnsConfig& d) {
attempts = d.attempts;
rotate = d.rotate;
edns0 = d.edns0;
+ use_local_ipv6 = d.use_local_ipv6;
}
base::Value* DnsConfig::ToValue() const {
@@ -73,6 +76,7 @@ base::Value* DnsConfig::ToValue() const {
dict->SetInteger("attempts", attempts);
dict->SetBoolean("rotate", rotate);
dict->SetBoolean("edns0", edns0);
+ dict->SetBoolean("use_local_ipv6", use_local_ipv6);
dict->SetInteger("num_hosts", hosts.size());
return dict;
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index d14a2cd66f..7386e60179 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -83,6 +83,11 @@ struct NET_EXPORT_PRIVATE DnsConfig {
bool rotate;
// Enable EDNS0 extensions.
bool edns0;
+
+ // Indicates system configuration uses local IPv6 connectivity, e.g.,
+ // DirectAccess. This is exposed for HostResolver to skip IPv6 probes,
+ // as it may cause them to return incorrect results.
+ bool use_local_ipv6;
};
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 6d12155d8c..fe97b74f73 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -43,8 +43,19 @@ namespace {
// Interval between retries to parse config. Used only until parsing succeeds.
const int kRetryIntervalSeconds = 5;
+// Registry key paths.
+const wchar_t* const kTcpipPath =
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
+const wchar_t* const kTcpip6Path =
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
+const wchar_t* const kDnscachePath =
+ L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
+const wchar_t* const kPolicyPath =
+ L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
const wchar_t* const kPrimaryDnsSuffixPath =
L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
+const wchar_t* const kNRPTPath =
+ L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient\\DnsPolicyConfig";
enum HostsParseWinResult {
HOSTS_PARSE_WIN_OK = 0,
@@ -198,6 +209,10 @@ ConfigParseWinResult ReadSystemSettings(DnsSystemSettings* settings) {
&settings->primary_dns_suffix)) {
return CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX;
}
+
+ base::win::RegistryKeyIterator nrpt_rules(HKEY_LOCAL_MACHINE, kNRPTPath);
+ settings->have_name_resolution_policy = (nrpt_rules.SubkeyCount() > 0);
+
return CONFIG_PARSE_WIN_OK;
}
@@ -330,8 +345,7 @@ bool IsStatelessDiscoveryAddress(const IPAddressNumber& address) {
address.begin()) && (address.back() < 4);
}
-} // namespace
-
+// Returns the path to the HOSTS file.
base::FilePath GetHostsPath() {
TCHAR buffer[MAX_PATH];
UINT rc = GetSystemDirectory(buffer, MAX_PATH);
@@ -340,6 +354,92 @@ base::FilePath GetHostsPath() {
FILE_PATH_LITERAL("drivers\\etc\\hosts"));
}
+void ConfigureSuffixSearch(const DnsSystemSettings& settings,
+ DnsConfig* config) {
+ // SearchList takes precedence, so check it first.
+ if (settings.policy_search_list.set) {
+ std::vector<std::string> search;
+ if (ParseSearchList(settings.policy_search_list.value, &search)) {
+ config->search.swap(search);
+ return;
+ }
+ // Even if invalid, the policy disables the user-specified setting below.
+ } else if (settings.tcpip_search_list.set) {
+ std::vector<std::string> search;
+ if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
+ config->search.swap(search);
+ return;
+ }
+ }
+
+ // In absence of explicit search list, suffix search is:
+ // [primary suffix, connection-specific suffix, devolution of primary suffix].
+ // Primary suffix can be set by policy (primary_dns_suffix) or
+ // user setting (tcpip_domain).
+ //
+ // The policy (primary_dns_suffix) can be edited via Group Policy Editor
+ // (gpedit.msc) at Local Computer Policy => Computer Configuration
+ // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
+ //
+ // The user setting (tcpip_domain) can be configurred at Computer Name in
+ // System Settings
+ std::string primary_suffix;
+ if ((settings.primary_dns_suffix.set &&
+ ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) ||
+ (settings.tcpip_domain.set &&
+ ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) {
+ // Primary suffix goes in front.
+ config->search.insert(config->search.begin(), primary_suffix);
+ } else {
+ return; // No primary suffix, hence no devolution.
+ }
+
+ // Devolution is determined by precedence: policy > dnscache > tcpip.
+ // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
+ // are overridden independently.
+ DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution;
+
+ if (!devolution.enabled.set)
+ devolution.enabled = settings.dnscache_devolution.enabled;
+ if (!devolution.enabled.set)
+ devolution.enabled = settings.tcpip_devolution.enabled;
+ if (devolution.enabled.set && (devolution.enabled.value == 0))
+ return; // Devolution disabled.
+
+ // By default devolution is enabled.
+
+ if (!devolution.level.set)
+ devolution.level = settings.dnscache_devolution.level;
+ if (!devolution.level.set)
+ devolution.level = settings.tcpip_devolution.level;
+
+ // After the recent update, Windows will try to determine a safe default
+ // value by comparing the forest root domain (FRD) to the primary suffix.
+ // See http://support.microsoft.com/kb/957579 for details.
+ // For now, if the level is not set, we disable devolution, assuming that
+ // we will fallback to the system getaddrinfo anyway. This might cause
+ // performance loss for resolutions which depend on the system default
+ // devolution setting.
+ //
+ // If the level is explicitly set below 2, devolution is disabled.
+ if (!devolution.level.set || devolution.level.value < 2)
+ return; // Devolution disabled.
+
+ // Devolve the primary suffix. This naive logic matches the observed
+ // behavior (see also ParseSearchList). If a suffix is not valid, it will be
+ // discarded when the fully-qualified name is converted to DNS format.
+
+ unsigned num_dots = std::count(primary_suffix.begin(),
+ primary_suffix.end(), '.');
+
+ for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) {
+ offset = primary_suffix.find('.', offset + 1);
+ config->search.push_back(primary_suffix.substr(offset + 1));
+ }
+}
+
+} // namespace
+
bool ParseSearchList(const base::string16& value,
std::vector<std::string>* output) {
DCHECK(output);
@@ -429,87 +529,16 @@ ConfigParseWinResult ConvertSettingsToDnsConfig(
(settings.append_to_multi_label_name.value != 0);
}
- // SearchList takes precedence, so check it first.
- if (settings.policy_search_list.set) {
- std::vector<std::string> search;
- if (ParseSearchList(settings.policy_search_list.value, &search)) {
- config->search.swap(search);
- return CONFIG_PARSE_WIN_OK;
- }
- // Even if invalid, the policy disables the user-specified setting below.
- } else if (settings.tcpip_search_list.set) {
- std::vector<std::string> search;
- if (ParseSearchList(settings.tcpip_search_list.value, &search)) {
- config->search.swap(search);
- return CONFIG_PARSE_WIN_OK;
- }
+ ConfigParseWinResult result = CONFIG_PARSE_WIN_OK;
+ if (settings.have_name_resolution_policy) {
+ config->unhandled_options = true;
+ // TODO(szym): only set this to true if NRPT has DirectAccess rules.
+ config->use_local_ipv6 = true;
+ result = CONFIG_PARSE_WIN_UNHANDLED_OPTIONS;
}
- // In absence of explicit search list, suffix search is:
- // [primary suffix, connection-specific suffix, devolution of primary suffix].
- // Primary suffix can be set by policy (primary_dns_suffix) or
- // user setting (tcpip_domain).
- //
- // The policy (primary_dns_suffix) can be edited via Group Policy Editor
- // (gpedit.msc) at Local Computer Policy => Computer Configuration
- // => Administrative Template => Network => DNS Client => Primary DNS Suffix.
- //
- // The user setting (tcpip_domain) can be configurred at Computer Name in
- // System Settings
- std::string primary_suffix;
- if ((settings.primary_dns_suffix.set &&
- ParseDomainASCII(settings.primary_dns_suffix.value, &primary_suffix)) ||
- (settings.tcpip_domain.set &&
- ParseDomainASCII(settings.tcpip_domain.value, &primary_suffix))) {
- // Primary suffix goes in front.
- config->search.insert(config->search.begin(), primary_suffix);
- } else {
- return CONFIG_PARSE_WIN_OK; // No primary suffix, hence no devolution.
- }
-
- // Devolution is determined by precedence: policy > dnscache > tcpip.
- // |enabled|: UseDomainNameDevolution and |level|: DomainNameDevolutionLevel
- // are overridden independently.
- DnsSystemSettings::DevolutionSetting devolution = settings.policy_devolution;
-
- if (!devolution.enabled.set)
- devolution.enabled = settings.dnscache_devolution.enabled;
- if (!devolution.enabled.set)
- devolution.enabled = settings.tcpip_devolution.enabled;
- if (devolution.enabled.set && (devolution.enabled.value == 0))
- return CONFIG_PARSE_WIN_OK; // Devolution disabled.
-
- // By default devolution is enabled.
-
- if (!devolution.level.set)
- devolution.level = settings.dnscache_devolution.level;
- if (!devolution.level.set)
- devolution.level = settings.tcpip_devolution.level;
-
- // After the recent update, Windows will try to determine a safe default
- // value by comparing the forest root domain (FRD) to the primary suffix.
- // See http://support.microsoft.com/kb/957579 for details.
- // For now, if the level is not set, we disable devolution, assuming that
- // we will fallback to the system getaddrinfo anyway. This might cause
- // performance loss for resolutions which depend on the system default
- // devolution setting.
- //
- // If the level is explicitly set below 2, devolution is disabled.
- if (!devolution.level.set || devolution.level.value < 2)
- return CONFIG_PARSE_WIN_OK; // Devolution disabled.
-
- // Devolve the primary suffix. This naive logic matches the observed
- // behavior (see also ParseSearchList). If a suffix is not valid, it will be
- // discarded when the fully-qualified name is converted to DNS format.
-
- unsigned num_dots = std::count(primary_suffix.begin(),
- primary_suffix.end(), '.');
-
- for (size_t offset = 0; num_dots >= devolution.level.value; --num_dots) {
- offset = primary_suffix.find('.', offset + 1);
- config->search.push_back(primary_suffix.substr(offset + 1));
- }
- return CONFIG_PARSE_WIN_OK;
+ ConfigureSuffixSearch(settings, config);
+ return result;
}
// Watches registry and HOSTS file for changes. Must live on a thread which
@@ -606,7 +635,8 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
ConfigParseWinResult result = ReadSystemSettings(&settings);
if (result == CONFIG_PARSE_WIN_OK)
result = ConvertSettingsToDnsConfig(settings, &dns_config_);
- success_ = (result == CONFIG_PARSE_WIN_OK);
+ success_ = (result == CONFIG_PARSE_WIN_OK ||
+ result == CONFIG_PARSE_WIN_UNHANDLED_OPTIONS);
UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParseWin",
result, CONFIG_PARSE_WIN_MAX);
UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 06fc0d9663..9503dc8593 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -34,19 +34,6 @@ namespace net {
namespace internal {
-// Registry key paths.
-const wchar_t* const kTcpipPath =
- L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
-const wchar_t* const kTcpip6Path =
- L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
-const wchar_t* const kDnscachePath =
- L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
-const wchar_t* const kPolicyPath =
- L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
-
-// Returns the path to the HOSTS file.
-base::FilePath GetHostsPath();
-
// Parses |value| as search list (comma-delimited list of domain names) from
// a registry key and stores it in |out|. Returns true on success. Empty
// entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
@@ -98,6 +85,10 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings {
// SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\AppendToMultiLabelName
RegDword append_to_multi_label_name;
+
+ // True when the Name Resolution Policy Table (NRPT) has at least one rule:
+ // SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig\Rule*
+ bool have_name_resolution_policy;
};
enum ConfigParseWinResult {
@@ -113,6 +104,7 @@ enum ConfigParseWinResult {
CONFIG_PARSE_WIN_READ_PRIMARY_SUFFIX,
CONFIG_PARSE_WIN_BAD_ADDRESS,
CONFIG_PARSE_WIN_NO_NAMESERVERS,
+ CONFIG_PARSE_WIN_UNHANDLED_OPTIONS,
CONFIG_PARSE_WIN_MAX // Bounding values for enumeration.
};
diff --git a/net/dns/dns_config_service_win_unittest.cc b/net/dns/dns_config_service_win_unittest.cc
index b28b8e9554..3f3e4ed1e3 100644
--- a/net/dns/dns_config_service_win_unittest.cc
+++ b/net/dns/dns_config_service_win_unittest.cc
@@ -4,6 +4,7 @@
#include "net/dns/dns_config_service_win.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/win/windows_version.h"
#include "net/dns/dns_protocol.h"
@@ -420,10 +421,46 @@ TEST(DnsConfigServiceWinTest, AppendToMultiLabelName) {
DnsConfig config;
EXPECT_EQ(internal::CONFIG_PARSE_WIN_OK,
internal::ConvertSettingsToDnsConfig(settings, &config));
- EXPECT_EQ(config.append_to_multi_label_name, t.expected_output);
+ EXPECT_EQ(t.expected_output, config.append_to_multi_label_name);
}
}
+// Setting have_name_resolution_policy_table should set unhandled_options.
+TEST(DnsConfigServiceWinTest, HaveNRPT) {
+ AdapterInfo infos[2] = {
+ { IF_TYPE_USB, IfOperStatusUp, L"connection.suffix", { "1.0.0.1" } },
+ { 0 },
+ };
+
+ const struct TestCase {
+ bool have_nrpt;
+ bool unhandled_options;
+ internal::ConfigParseWinResult result;
+ } cases[] = {
+ { false, false, internal::CONFIG_PARSE_WIN_OK },
+ { true, true, internal::CONFIG_PARSE_WIN_UNHANDLED_OPTIONS },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ const TestCase& t = cases[i];
+ internal::DnsSystemSettings settings = {
+ CreateAdapterAddresses(infos),
+ { false }, { false }, { false }, { false },
+ { { false }, { false } },
+ { { false }, { false } },
+ { { false }, { false } },
+ { false },
+ t.have_nrpt,
+ };
+ DnsConfig config;
+ EXPECT_EQ(t.result,
+ internal::ConvertSettingsToDnsConfig(settings, &config));
+ EXPECT_EQ(t.unhandled_options, config.unhandled_options);
+ EXPECT_EQ(t.have_nrpt, config.use_local_ipv6);
+ }
+}
+
+
} // namespace
} // namespace net
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 52c6f93bb5..e4098d003b 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -1455,8 +1455,12 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job,
queue_time_after_change);
}
+ bool system_only =
+ (key_.host_resolver_flags & HOST_RESOLVER_SYSTEM_ONLY) != 0;
+
// Caution: Job::Start must not complete synchronously.
- if (had_dns_config_ && !ResemblesMulticastDNSName(key_.hostname)) {
+ if (!system_only && had_dns_config_ &&
+ !ResemblesMulticastDNSName(key_.hostname)) {
StartDnsTask();
} else {
StartProcTask();
@@ -1799,6 +1803,7 @@ HostResolverImpl::HostResolverImpl(
received_dns_config_(false),
num_dns_failures_(0),
probe_ipv6_support_(true),
+ use_local_ipv6_(false),
resolved_known_ipv6_hostname_(false),
additional_resolver_flags_(0),
fallback_to_proctask_(true) {
@@ -1824,12 +1829,12 @@ HostResolverImpl::HostResolverImpl(
EnsureDnsReloaderInit();
#endif
- // TODO(szym): Remove when received_dns_config_ is removed, once
- // http://crbug.com/137914 is resolved.
{
DnsConfig dns_config;
NetworkChangeNotifier::GetDnsConfig(&dns_config);
received_dns_config_ = dns_config.IsValid();
+ // Conservatively assume local IPv6 is needed when DnsConfig is not valid.
+ use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6;
}
fallback_to_proctask_ = !ConfigureAsyncDnsNoFallbackFieldTrial();
@@ -2142,7 +2147,7 @@ HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
AddressFamily effective_address_family = info.address_family();
if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) {
- if (probe_ipv6_support_) {
+ if (probe_ipv6_support_ && !use_local_ipv6_) {
base::TimeTicks start_time = base::TimeTicks::Now();
// Google DNS address.
const uint8 kIPv6Address[] =
@@ -2266,6 +2271,8 @@ void HostResolverImpl::OnDNSChanged() {
// TODO(szym): Remove once http://crbug.com/137914 is resolved.
received_dns_config_ = dns_config.IsValid();
+ // Conservatively assume local IPv6 is needed when DnsConfig is not valid.
+ use_local_ipv6_ = !dns_config.IsValid() || dns_config.use_local_ipv6;
num_dns_failures_ = 0;
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h
index 2d93258b16..e7f5f6541c 100644
--- a/net/dns/host_resolver_impl.h
+++ b/net/dns/host_resolver_impl.h
@@ -279,6 +279,10 @@ class NET_EXPORT HostResolverImpl
// explicit setting in |default_address_family_| is used.
bool probe_ipv6_support_;
+ // True if DnsConfigService detected that system configuration depends on
+ // local IPv6 connectivity. Disables probing.
+ bool use_local_ipv6_;
+
// True iff ProcTask has successfully resolved a hostname known to have IPv6
// addresses using ADDRESS_FAMILY_UNSPECIFIED. Reset on IP address change.
bool resolved_known_ipv6_hostname_;
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 24a00a9d69..c314f80c24 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -1563,6 +1563,24 @@ TEST_F(HostResolverImplDnsTest, BypassDnsTask) {
EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
}
+TEST_F(HostResolverImplDnsTest, SystemOnlyBypassesDnsTask) {
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ proc_->AddRuleForAllFamilies(std::string(), std::string());
+
+ HostResolver::RequestInfo info_bypass(HostPortPair("ok", 80));
+ info_bypass.set_host_resolver_flags(HOST_RESOLVER_SYSTEM_ONLY);
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info_bypass, MEDIUM)->Resolve());
+
+ HostResolver::RequestInfo info(HostPortPair("ok", 80));
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info, MEDIUM)->Resolve());
+
+ proc_->SignalMultiple(requests_.size());
+
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[0]->WaitForResult());
+ EXPECT_EQ(OK, requests_[1]->WaitForResult());
+}
+
TEST_F(HostResolverImplDnsTest, DisableDnsClientOnPersistentFailure) {
ChangeDnsConfig(CreateValidDnsConfig());
diff --git a/net/dns/mdns_client.cc b/net/dns/mdns_client.cc
index 631b01a706..d0273c5784 100644
--- a/net/dns/mdns_client.cc
+++ b/net/dns/mdns_client.cc
@@ -4,14 +4,43 @@
#include "net/dns/mdns_client.h"
+#include "net/dns/dns_protocol.h"
#include "net/dns/mdns_client_impl.h"
namespace net {
+namespace {
+
+const char kMDnsMulticastGroupIPv4[] = "224.0.0.251";
+const char kMDnsMulticastGroupIPv6[] = "FF02::FB";
+
+IPEndPoint GetMDnsIPEndPoint(const char* address) {
+ IPAddressNumber multicast_group_number;
+ bool success = ParseIPLiteralToNumber(address,
+ &multicast_group_number);
+ DCHECK(success);
+ return IPEndPoint(multicast_group_number,
+ dns_protocol::kDefaultPortMulticast);
+}
+
+} // namespace
+
// static
scoped_ptr<MDnsClient> MDnsClient::CreateDefault() {
return scoped_ptr<MDnsClient>(
new MDnsClientImpl(MDnsConnection::SocketFactory::CreateDefault()));
}
+IPEndPoint GetMDnsIPEndPoint(AddressFamily address_family) {
+ switch (address_family) {
+ case ADDRESS_FAMILY_IPV4:
+ return GetMDnsIPEndPoint(kMDnsMulticastGroupIPv4);
+ case ADDRESS_FAMILY_IPV6:
+ return GetMDnsIPEndPoint(kMDnsMulticastGroupIPv6);
+ default:
+ NOTREACHED();
+ return IPEndPoint();
+ }
+}
+
} // namespace net
diff --git a/net/dns/mdns_client.h b/net/dns/mdns_client.h
index f12c6e292c..71006d69b0 100644
--- a/net/dns/mdns_client.h
+++ b/net/dns/mdns_client.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/callback.h"
+#include "net/base/ip_endpoint.h"
#include "net/dns/dns_query.h"
#include "net/dns/dns_response.h"
#include "net/dns/record_parsed.h"
@@ -154,5 +155,7 @@ class NET_EXPORT MDnsClient {
static scoped_ptr<MDnsClient> CreateDefault();
};
+IPEndPoint NET_EXPORT GetMDnsIPEndPoint(AddressFamily address_family);
+
} // namespace net
#endif // NET_DNS_MDNS_CLIENT_H_
diff --git a/net/dns/mdns_client_impl.cc b/net/dns/mdns_client_impl.cc
index d8e5e9b760..4a3046847d 100644
--- a/net/dns/mdns_client_impl.cc
+++ b/net/dns/mdns_client_impl.cc
@@ -24,8 +24,6 @@
namespace net {
namespace {
-const char kMDnsMulticastGroupIPv4[] = "224.0.0.251";
-const char kMDnsMulticastGroupIPv6[] = "FF02::FB";
const unsigned MDnsTransactionTimeoutSeconds = 3;
}
@@ -99,11 +97,9 @@ int MDnsConnection::SocketHandler::Bind() {
MDnsConnection::MDnsConnection(MDnsConnection::SocketFactory* socket_factory,
MDnsConnection::Delegate* delegate)
- : socket_handler_ipv4_(this,
- GetMDnsIPEndPoint(kMDnsMulticastGroupIPv4),
+ : socket_handler_ipv4_(this, GetMDnsIPEndPoint(ADDRESS_FAMILY_IPV4),
socket_factory),
- socket_handler_ipv6_(this,
- GetMDnsIPEndPoint(kMDnsMulticastGroupIPv6),
+ socket_handler_ipv6_(this, GetMDnsIPEndPoint(ADDRESS_FAMILY_IPV6),
socket_factory),
delegate_(delegate) {
}
@@ -146,15 +142,6 @@ void MDnsConnection::OnError(SocketHandler* loop,
delegate_->OnConnectionError(error);
}
-IPEndPoint MDnsConnection::GetMDnsIPEndPoint(const char* address) {
- IPAddressNumber multicast_group_number;
- bool success = ParseIPLiteralToNumber(address,
- &multicast_group_number);
- DCHECK(success);
- return IPEndPoint(multicast_group_number,
- dns_protocol::kDefaultPortMulticast);
-}
-
void MDnsConnection::OnDatagramReceived(
DnsResponse* response,
const IPEndPoint& recv_addr,
diff --git a/net/dns/mdns_client_impl.h b/net/dns/mdns_client_impl.h
index 86a7ba9b36..cbb9f6da3e 100644
--- a/net/dns/mdns_client_impl.h
+++ b/net/dns/mdns_client_impl.h
@@ -85,8 +85,6 @@ class NET_EXPORT_PRIVATE MDnsConnection {
void OnError(SocketHandler* loop, int error);
- IPEndPoint GetMDnsIPEndPoint(const char* address);
-
SocketHandler socket_handler_ipv4_;
SocketHandler socket_handler_ipv6_;
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index a1d46d06d3..ff5ffbc2f7 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -347,12 +347,14 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host,
bool matches_address_family =
r->address_family == ADDRESS_FAMILY_UNSPECIFIED ||
r->address_family == address_family;
+ // Ignore HOST_RESOLVER_SYSTEM_ONLY, since it should have no impact on
+ // whether a rule matches.
+ HostResolverFlags flags = host_resolver_flags & ~HOST_RESOLVER_SYSTEM_ONLY;
// Flags match if all of the bitflags in host_resolver_flags are enabled
// in the rule's host_resolver_flags. However, the rule may have additional
// flags specified, in which case the flags should still be considered a
// match.
- bool matches_flags = (r->host_resolver_flags & host_resolver_flags) ==
- host_resolver_flags;
+ bool matches_flags = (r->host_resolver_flags & flags) == flags;
if (matches_flags && matches_address_family &&
MatchPattern(host, r->host_pattern)) {
if (r->latency_ms != 0) {
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 4cdcbb6cd8..49591da8ff 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -392,7 +392,7 @@ void HttpCache::CloseAllConnections() {
HttpNetworkSession* session = network->GetSession();
if (session)
session->CloseAllConnections();
- }
+}
void HttpCache::CloseIdleConnections() {
net::HttpNetworkLayer* network =
@@ -595,6 +595,9 @@ int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
}
void HttpCache::DoomMainEntryForUrl(const GURL& url) {
+ if (!disk_cache_)
+ return;
+
HttpRequestInfo temp_info;
temp_info.url = url;
temp_info.method = "GET";
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index a69bcc0b43..a710afb3c3 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -449,10 +449,13 @@ bool HttpCache::Transaction::GetFullRequestHeaders(
void HttpCache::Transaction::DoneReading() {
if (cache_.get() && entry_) {
- DCHECK(reading_);
DCHECK_NE(mode_, UPDATE);
- if (mode_ & WRITE)
+ if (mode_ & WRITE) {
DoneWritingToEntry(true);
+ } else {
+ cache_->DoneReadingFromEntry(entry_, this);
+ entry_ = NULL;
+ }
}
}
@@ -952,7 +955,7 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
mode_ = NONE;
}
- if (mode_ != NONE && request_->method == "POST" &&
+ if (request_->method == "POST" &&
NonErrorResponse(new_response->headers->response_code())) {
cache_->DoomMainEntryForUrl(request_->url);
}
@@ -1340,10 +1343,6 @@ int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) {
}
}
- // If this response is a redirect, then we can stop writing now. (We don't
- // need to cache the response body of a redirect.)
- if (response_.headers->IsRedirect(NULL))
- DoneWritingToEntry(true);
next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
return OK;
}
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 01909ae3c5..cc4874f624 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -2895,6 +2895,67 @@ TEST(HttpCache, SimplePOST_Invalidate_205) {
RemoveMockTransaction(&transaction);
}
+// Tests that a successful POST invalidates a previously cached GET, even when
+// there is no upload identifier.
+TEST(HttpCache, SimplePOST_NoUploadId_Invalidate_205) {
+ MockHttpCache cache;
+
+ MockTransaction transaction(kSimpleGET_Transaction);
+ AddMockTransaction(&transaction);
+ MockHttpRequest req1(transaction);
+
+ // Attempt to populate the cache.
+ RunTransactionTestWithRequest(cache.http_cache(), transaction, req1, NULL);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ ScopedVector<net::UploadElementReader> element_readers;
+ element_readers.push_back(new net::UploadBytesElementReader("hello", 5));
+ net::UploadDataStream upload_data_stream(&element_readers, 0);
+
+ transaction.method = "POST";
+ transaction.status = "HTTP/1.1 205 No Content";
+ MockHttpRequest req2(transaction);
+ req2.upload_data_stream = &upload_data_stream;
+
+ RunTransactionTestWithRequest(cache.http_cache(), transaction, req2, NULL);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ RunTransactionTestWithRequest(cache.http_cache(), transaction, req1, NULL);
+
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+ RemoveMockTransaction(&transaction);
+}
+
+// Tests that processing a POST before creating the backend doesn't crash.
+TEST(HttpCache, SimplePOST_NoUploadId_NoBackend) {
+ // This will initialize a cache object with NULL backend.
+ MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+ factory->set_fail(true);
+ factory->FinishCreation();
+ MockHttpCache cache(factory);
+
+ ScopedVector<net::UploadElementReader> element_readers;
+ element_readers.push_back(new net::UploadBytesElementReader("hello", 5));
+ net::UploadDataStream upload_data_stream(&element_readers, 0);
+
+ MockTransaction transaction(kSimplePOST_Transaction);
+ AddMockTransaction(&transaction);
+ MockHttpRequest req(transaction);
+ req.upload_data_stream = &upload_data_stream;
+
+ RunTransactionTestWithRequest(cache.http_cache(), transaction, req, NULL);
+
+ RemoveMockTransaction(&transaction);
+}
+
// Tests that we don't invalidate entries as a result of a failed POST.
TEST(HttpCache, SimplePOST_DontInvalidate_100) {
MockHttpCache cache;
@@ -5333,7 +5394,7 @@ TEST(HttpCache, CachedRedirect) {
MockHttpRequest request(kTestTransaction);
net::TestCompletionCallback callback;
- // write to the cache
+ // Write to the cache.
{
scoped_ptr<net::HttpTransaction> trans;
int rv = cache.http_cache()->CreateTransaction(
@@ -5355,6 +5416,9 @@ TEST(HttpCache, CachedRedirect) {
info->headers->EnumerateHeader(NULL, "Location", &location);
EXPECT_EQ(location, "http://www.bar.com/");
+ // Mark the transaction as completed so it is cached.
+ trans->DoneReading();
+
// Destroy transaction when going out of scope. We have not actually
// read the response body -- want to test that it is still getting cached.
}
@@ -5362,7 +5426,12 @@ TEST(HttpCache, CachedRedirect) {
EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
- // read from the cache
+ // Active entries in the cache are not retired synchronously. Make
+ // sure the next run hits the MockHttpCache and open_count is
+ // correct.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Read from the cache.
{
scoped_ptr<net::HttpTransaction> trans;
int rv = cache.http_cache()->CreateTransaction(
@@ -5384,6 +5453,9 @@ TEST(HttpCache, CachedRedirect) {
info->headers->EnumerateHeader(NULL, "Location", &location);
EXPECT_EQ(location, "http://www.bar.com/");
+ // Mark the transaction as completed so it is cached.
+ trans->DoneReading();
+
// Destroy transaction when going out of scope. We have not actually
// read the response body -- want to test that it is still getting cached.
}
@@ -5838,6 +5910,39 @@ TEST(HttpCache, FilterCompletion) {
EXPECT_EQ(1, cache.disk_cache()->create_count());
}
+// Tests that we don't mark entries as truncated and release the cache
+// entry when DoneReading() is called before any Read() calls, such as
+// for a redirect.
+TEST(HttpCache, DoneReading) {
+ MockHttpCache cache;
+ net::TestCompletionCallback callback;
+
+ ScopedMockTransaction transaction(kSimpleGET_Transaction);
+ transaction.data = "";
+
+ scoped_ptr<net::HttpTransaction> trans;
+ int rv = cache.http_cache()->CreateTransaction(
+ net::DEFAULT_PRIORITY, &trans, NULL);
+ EXPECT_EQ(net::OK, rv);
+
+ MockHttpRequest request(transaction);
+ rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
+ EXPECT_EQ(net::OK, callback.GetResult(rv));
+
+ trans->DoneReading();
+ // Leave the transaction around.
+
+ // Make sure that the ActiveEntry is gone.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Read from the cache. This should not deadlock.
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
// Tests that we stop caching when told.
TEST(HttpCache, StopCachingDeletesEntry) {
MockHttpCache cache;
diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h
index ec3fc088a0..d44050080b 100644
--- a/net/http/http_transaction.h
+++ b/net/http/http_transaction.h
@@ -109,6 +109,9 @@ class NET_EXPORT_PRIVATE HttpTransaction {
// of the stream. This is equivalent to performing an extra Read() at the end
// that should return 0 bytes. This method should not be called if the
// transaction is busy processing a previous operation (like a pending Read).
+ //
+ // DoneReading may also be called before the first Read() to notify that the
+ // entire response body is to be ignored (e.g., in a redirect).
virtual void DoneReading() = 0;
// Returns the response info for this transaction or NULL if the response
diff --git a/net/http/transport_security_state_static.certs b/net/http/transport_security_state_static.certs
index 2fdd9af801..90a11c97f1 100644
--- a/net/http/transport_security_state_static.certs
+++ b/net/http/transport_security_state_static.certs
@@ -106,6 +106,44 @@ q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR
bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv
-----END CERTIFICATE-----
+VeriSignClass3SSPIntermediateCA
+-----BEGIN CERTIFICATE-----
+MIIGVDCCBTygAwIBAgIQGYH0QFTS4OtUK7v7RciQfjANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzMwHhcNMTEwMTA3MDAwMDAwWhcNMTMxMjMxMjM1OTU5WjB2MQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxLTArBgNVBAMTJFZlcmlTaWduIENsYXNzIDMg
+U1NQIEludGVybWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANfMaBonchSI7reVYNNe3hhSwUY/fbEmnDwCoonR2MFXsQkP9n8yNaU1nhRT
+Eovg4zAetI+e0bDAt9/0Lw/n1x/FdiTTPdMN6SxKLqc8z7xql0MZ+MBzyhsstmIB
+RmJWkGisFFAZ51BYB/k9AfLtHjQnvc1yHYBgo0ySG2a6ejkJd2r6U/dvjgbu2dSj
+Eo5XJGl//xSSLKs4HPhkuAsdZr2HqPiBwjlFpCd//Fs8he43JBI60+bRSBiUKpQC
+ssu6oAj2rvKcy2AMTvjIAlz9Iy3B92fB1Q1JxpbWcLochUca7/NFQTkKMaVeBXxy
+i2D+SFWfuBLtcl7p/kbtwqfiDbMCAwEAAaOCAocwggKDMA8GA1UdEwEB/wQFMAMB
+Af8wDgYDVR0PAQH/BAQDAgEGMIHoBgNVHSAEgeAwgd0wDwYNYIZIAYb4RQEHFwMB
+BjAPBg1ghkgBhvhFAQcXAwEHMA8GDWCGSAGG+EUBBxcDAQgwDwYNYIZIAYb4RQEH
+FwMBDTAPBg1ghkgBhvhFAQcXAwEOMA8GDWCGSAGG+EUBBxcDAQ8wDwYNYIZIAYb4
+RQEHFwMBETAPBg1ghkgBhvhFAQcXAwEUMA8GDWCGSAGG+EUBBxcDARcwDwYNYIZI
+AYb4RQEHFwMBGDAPBg1ghkgBhvhFAQcXAwEZMA8GDWCGSAGG+EUBBxcDARowDwYN
+YIZIAYb4RQEHFwMBGzA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vc3NwLWNybC52
+ZXJpc2lnbi5jb20vcGNhMy1nMy5jcmwwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMT
+EFZlcmlTaWduTVBLSS0xLTgwHQYDVR0OBBYEFCwx/8HOq/lN6IkVwGry5atCfUL6
+MIHxBgNVHSMEgekwgeahgdCkgc0wgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5W
+ZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6
+MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXpl
+ZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMyBQdWJsaWMgUHJp
+bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczghEAm34GSaM+YrnV7pBI
+cSnvVzANBgkqhkiG9w0BAQUFAAOCAQEAIS19vzG9j+KXiQ0G1bOuJCeiD9KKW1+8
+69cutvgDf3hEvrw39Gr2ek3cAdso7dvwW0Z17muzpHV08gWTjjKba8mBzjijmgr9
+I2vE2K/Ls72WJvTDUjCAHfBJKeK1q8v7xv1xtf2Jz7BV8sNH3kDB7jhhE++8zLVC
+gyFilU0KZfhBpLPVlVYnLozRdvsHfNnO/JskJvRqhDYbeC5ginQT0m5sTQiyTYqL
+/IU+i82TxANXjC7syl0dfcGr8pJ85T9bF1EZLxdgikAYLKPGTuXMwOGqT5bR0dKD
+lWShiGTRl7HW0KJMg05F0HjOnYpdOYGaFrQghecrkcrRPRevSdFVHQ==
+-----END CERTIFICATE-----
+
EquifaxSecureCA
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h
index eb72813753..36289ccd4d 100644
--- a/net/http/transport_security_state_static.h
+++ b/net/http/transport_security_state_static.h
@@ -46,6 +46,10 @@ static const char kSPKIHash_ThawteSGCCA[] =
"\x87\x31\xea\x0e\x3d\xf5\xe8\x70\x3e\x83"
"\x72\x57\x77\xa9\x65\x3b\x3b\xfa\x5e\x14";
+static const char kSPKIHash_VeriSignClass3SSPIntermediateCA[] =
+ "\x99\x6a\x20\x6a\x85\x57\x62\xcb\x9a\xf2"
+ "\x02\x37\xb3\xc0\x69\x5d\xa9\x1e\xc2\x22";
+
static const char kSPKIHash_EquifaxSecureCA[] =
"\x48\xe6\x68\xf9\x2b\xd2\xb2\x95\xd7\x47"
"\xd8\x23\x20\x10\x4f\x33\x98\x90\x9f\xd4";
@@ -277,6 +281,7 @@ static const char* const kGoogleRejectedCerts[] = {
kSPKIHash_TCTrustCenter,
kSPKIHash_Vodafone,
kSPKIHash_ThawteSGCCA,
+ kSPKIHash_VeriSignClass3SSPIntermediateCA,
NULL,
};
#define kGooglePins { \
@@ -862,6 +867,9 @@ static const struct HSTSPreload kPreloadedSTS[] = {
{12, false, "\006bcrook\003com", true, kNoPins, DOMAIN_NOT_PINNED },
{17, true, "\004wiki\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED },
{9, false, "\004lumi\002do", true, kNoPins, DOMAIN_NOT_PINNED },
+ {22, true, "\020appseccalifornia\003org", true, kNoPins, DOMAIN_NOT_PINNED },
+ {17, true, "\013crowdcurity\003com", true, kNoPins, DOMAIN_NOT_PINNED },
+ {19, true, "\013saturngames\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED },
};
static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 085e872109..8e8e7ee86a 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -57,7 +57,8 @@
"Intel",
"TCTrustCenter",
"Vodafone",
- "ThawteSGCCA"
+ "ThawteSGCCA",
+ "VeriSignClass3SSPIntermediateCA"
]
},
{
@@ -640,6 +641,9 @@
{ "name": "bcrook.com", "mode": "force-https" },
{ "name": "wiki.python.org", "include_subdomains": true, "mode": "force-https" },
{ "name": "lumi.do", "mode": "force-https" },
+ { "name": "appseccalifornia.org", "include_subdomains": true, "mode": "force-https" },
+ { "name": "crowdcurity.com", "include_subdomains": true, "mode": "force-https" },
+ { "name": "saturngames.co.uk", "include_subdomains": true, "mode": "force-https" },
// Entries that are only valid if the client supports SNI.
{ "name": "gmail.com", "mode": "force-https", "pins": "google", "snionly": true },
diff --git a/net/http_server.target.darwin-arm.mk b/net/http_server.target.darwin-arm.mk
index 343421b557..5effb06f23 100644
--- a/net/http_server.target.darwin-arm.mk
+++ b/net/http_server.target.darwin-arm.mk
@@ -65,7 +65,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -150,7 +150,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/http_server.target.darwin-mips.mk b/net/http_server.target.darwin-mips.mk
index dfc92f44fa..6694afb87d 100644
--- a/net/http_server.target.darwin-mips.mk
+++ b/net/http_server.target.darwin-mips.mk
@@ -64,7 +64,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -148,7 +148,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/http_server.target.darwin-x86.mk b/net/http_server.target.darwin-x86.mk
index 5c8201f340..9649d4fcbc 100644
--- a/net/http_server.target.darwin-x86.mk
+++ b/net/http_server.target.darwin-x86.mk
@@ -67,7 +67,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -154,7 +154,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/http_server.target.linux-arm.mk b/net/http_server.target.linux-arm.mk
index 343421b557..5effb06f23 100644
--- a/net/http_server.target.linux-arm.mk
+++ b/net/http_server.target.linux-arm.mk
@@ -65,7 +65,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -150,7 +150,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/http_server.target.linux-mips.mk b/net/http_server.target.linux-mips.mk
index dfc92f44fa..6694afb87d 100644
--- a/net/http_server.target.linux-mips.mk
+++ b/net/http_server.target.linux-mips.mk
@@ -64,7 +64,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -148,7 +148,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/http_server.target.linux-x86.mk b/net/http_server.target.linux-x86.mk
index 5c8201f340..9649d4fcbc 100644
--- a/net/http_server.target.linux-x86.mk
+++ b/net/http_server.target.linux-x86.mk
@@ -67,7 +67,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -154,7 +154,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.gyp b/net/net.gyp
index dae94a22e9..c7a49dd8c5 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -390,8 +390,10 @@
'disk_cache/tracing_cache_backend.h',
'disk_cache/simple/simple_backend_impl.cc',
'disk_cache/simple/simple_backend_impl.h',
+ 'disk_cache/simple/simple_backend_version.h',
'disk_cache/simple/simple_entry_format.cc',
'disk_cache/simple/simple_entry_format.h',
+ 'disk_cache/simple/simple_entry_format_history.h',
'disk_cache/simple/simple_entry_impl.cc',
'disk_cache/simple/simple_entry_impl.h',
'disk_cache/simple/simple_entry_operation.cc',
@@ -409,6 +411,8 @@
'disk_cache/simple/simple_synchronous_entry.h',
'disk_cache/simple/simple_util.cc',
'disk_cache/simple/simple_util.h',
+ 'disk_cache/simple/simple_version_upgrade.cc',
+ 'disk_cache/simple/simple_version_upgrade.h',
'disk_cache/flash/flash_entry_impl.cc',
'disk_cache/flash/flash_entry_impl.h',
'disk_cache/flash/format.h',
@@ -422,6 +426,8 @@
'disk_cache/flash/segment.h',
'disk_cache/flash/storage.cc',
'disk_cache/flash/storage.h',
+ 'disk_cache/v3/block_bitmaps.cc',
+ 'disk_cache/v3/block_bitmaps.h',
'disk_cache/v3/disk_format_v3.h',
'dns/address_sorter.h',
'dns/address_sorter_posix.cc',
@@ -823,6 +829,8 @@
'quic/quic_framer.h',
'quic/quic_http_stream.cc',
'quic/quic_http_stream.h',
+ 'quic/quic_http_utils.cc',
+ 'quic/quic_http_utils.h',
'quic/quic_packet_creator.cc',
'quic/quic_packet_creator.h',
'quic/quic_packet_generator.cc',
@@ -835,6 +843,8 @@
'quic/quic_reliable_client_stream.h',
'quic/quic_sent_entropy_manager.cc',
'quic/quic_sent_entropy_manager.h',
+ 'quic/quic_sent_packet_manager.cc',
+ 'quic/quic_sent_packet_manager.h',
'quic/quic_session.cc',
'quic/quic_session.h',
'quic/quic_spdy_compressor.cc',
@@ -905,14 +915,11 @@
'socket/stream_socket.h',
'socket/tcp_client_socket.cc',
'socket/tcp_client_socket.h',
- 'socket/tcp_client_socket_libevent.cc',
- 'socket/tcp_client_socket_libevent.h',
- 'socket/tcp_client_socket_win.cc',
- 'socket/tcp_client_socket_win.h',
'socket/tcp_listen_socket.cc',
'socket/tcp_listen_socket.h',
'socket/tcp_server_socket.cc',
'socket/tcp_server_socket.h',
+ 'socket/tcp_socket.cc',
'socket/tcp_socket.h',
'socket/tcp_socket_libevent.cc',
'socket/tcp_socket_libevent.h',
@@ -1103,6 +1110,10 @@
'websockets/websocket_deflater.cc',
'websockets/websocket_errors.cc',
'websockets/websocket_errors.h',
+ 'websockets/websocket_extension.cc',
+ 'websockets/websocket_extension.h',
+ 'websockets/websocket_extension_parser.cc',
+ 'websockets/websocket_extension_parser.h',
'websockets/websocket_frame.cc',
'websockets/websocket_frame.h',
'websockets/websocket_frame_parser.cc',
@@ -1374,8 +1385,6 @@
[ 'OS == "win"', {
'sources!': [
'http/http_auth_handler_ntlm_portable.cc',
- 'socket/tcp_client_socket_libevent.cc',
- 'socket/tcp_client_socket_libevent.h',
'socket/tcp_socket_libevent.cc',
'socket/tcp_socket_libevent.h',
'ssl/client_cert_store_impl_nss.cc',
@@ -1577,11 +1586,13 @@
'disk_cache/simple/simple_test_util.h',
'disk_cache/simple/simple_test_util.cc',
'disk_cache/simple/simple_util_unittest.cc',
+ 'disk_cache/simple/simple_version_upgrade_unittest.cc',
'disk_cache/storage_block_unittest.cc',
'disk_cache/flash/log_store_entry_unittest.cc',
'disk_cache/flash/log_store_unittest.cc',
'disk_cache/flash/segment_unittest.cc',
'disk_cache/flash/storage_unittest.cc',
+ 'disk_cache/v3/block_bitmaps_unittest.cc',
'dns/address_sorter_posix_unittest.cc',
'dns/address_sorter_unittest.cc',
'dns/dns_config_service_posix_unittest.cc',
@@ -1767,6 +1778,7 @@
'quic/quic_fec_group_test.cc',
'quic/quic_framer_test.cc',
'quic/quic_http_stream_test.cc',
+ 'quic/quic_http_utils_test.cc',
'quic/quic_network_transaction_unittest.cc',
'quic/quic_packet_creator_test.cc',
'quic/quic_packet_generator_test.cc',
@@ -1774,6 +1786,7 @@
'quic/quic_received_packet_manager_test.cc',
'quic/quic_reliable_client_stream_test.cc',
'quic/quic_sent_entropy_manager_test.cc',
+ 'quic/quic_sent_packet_manager_test.cc',
'quic/quic_session_test.cc',
'quic/quic_spdy_compressor_test.cc',
'quic/quic_spdy_decompressor_test.cc',
@@ -1875,6 +1888,7 @@
'websockets/websocket_channel_test.cc',
'websockets/websocket_deflater_test.cc',
'websockets/websocket_errors_test.cc',
+ 'websockets/websocket_extension_parser_test.cc',
'websockets/websocket_frame_parser_test.cc',
'websockets/websocket_frame_test.cc',
'websockets/websocket_handshake_handler_test.cc',
@@ -1908,8 +1922,10 @@
'tools/quic/quic_in_memory_cache_test.cc',
'tools/quic/quic_reliable_client_stream_test.cc',
'tools/quic/quic_reliable_server_stream_test.cc',
+ 'tools/quic/quic_server_session_test.cc',
'tools/quic/quic_server_test.cc',
'tools/quic/quic_spdy_server_stream_test.cc',
+ 'tools/quic/quic_time_wait_list_manager_test.cc',
'tools/quic/test_tools/http_message_test_utils.cc',
'tools/quic/test_tools/http_message_test_utils.h',
'tools/quic/test_tools/mock_epoll_server.cc',
@@ -2005,6 +2021,9 @@
'socket/ssl_client_socket_openssl_unittest.cc',
'ssl/openssl_client_key_store_unittest.cc',
],
+ 'sources/': [
+ ['exclude', '^tools/flip_server'],
+ ],
},
],
[ 'enable_websockets != 1', {
diff --git a/net/net.target.darwin-arm.mk b/net/net.target.darwin-arm.mk
index d484b247fa..3bd52d9b88 100644
--- a/net/net.target.darwin-arm.mk
+++ b/net/net.target.darwin-arm.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -492,7 +498,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -587,7 +593,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.target.darwin-mips.mk b/net/net.target.darwin-mips.mk
index d7a3f17ecf..b52aecc91a 100644
--- a/net/net.target.darwin-mips.mk
+++ b/net/net.target.darwin-mips.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -491,7 +497,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -585,7 +591,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.target.darwin-x86.mk b/net/net.target.darwin-x86.mk
index af07ef16a9..7be29d9c5e 100644
--- a/net/net.target.darwin-x86.mk
+++ b/net/net.target.darwin-x86.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -494,7 +500,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -591,7 +597,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.target.linux-arm.mk b/net/net.target.linux-arm.mk
index d484b247fa..3bd52d9b88 100644
--- a/net/net.target.linux-arm.mk
+++ b/net/net.target.linux-arm.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -492,7 +498,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -587,7 +593,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.target.linux-mips.mk b/net/net.target.linux-mips.mk
index d7a3f17ecf..b52aecc91a 100644
--- a/net/net.target.linux-mips.mk
+++ b/net/net.target.linux-mips.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -491,7 +497,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -585,7 +591,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net.target.linux-x86.mk b/net/net.target.linux-x86.mk
index af07ef16a9..7be29d9c5e 100644
--- a/net/net.target.linux-x86.mk
+++ b/net/net.target.linux-x86.mk
@@ -158,12 +158,14 @@ LOCAL_SRC_FILES := \
net/disk_cache/simple/simple_net_log_parameters.cc \
net/disk_cache/simple/simple_synchronous_entry.cc \
net/disk_cache/simple/simple_util.cc \
+ net/disk_cache/simple/simple_version_upgrade.cc \
net/disk_cache/flash/flash_entry_impl.cc \
net/disk_cache/flash/internal_entry.cc \
net/disk_cache/flash/log_store.cc \
net/disk_cache/flash/log_store_entry.cc \
net/disk_cache/flash/segment.cc \
net/disk_cache/flash/storage.cc \
+ net/disk_cache/v3/block_bitmaps.cc \
net/dns/address_sorter_posix.cc \
net/dns/dns_client.cc \
net/dns/dns_config_service.cc \
@@ -328,12 +330,14 @@ LOCAL_SRC_FILES := \
net/quic/quic_fec_group.cc \
net/quic/quic_framer.cc \
net/quic/quic_http_stream.cc \
+ net/quic/quic_http_utils.cc \
net/quic/quic_packet_creator.cc \
net/quic/quic_packet_generator.cc \
net/quic/quic_protocol.cc \
net/quic/quic_received_packet_manager.cc \
net/quic/quic_reliable_client_stream.cc \
net/quic/quic_sent_entropy_manager.cc \
+ net/quic/quic_sent_packet_manager.cc \
net/quic/quic_session.cc \
net/quic/quic_spdy_compressor.cc \
net/quic/quic_spdy_decompressor.cc \
@@ -364,9 +368,9 @@ LOCAL_SRC_FILES := \
net/socket/stream_listen_socket.cc \
net/socket/stream_socket.cc \
net/socket/tcp_client_socket.cc \
- net/socket/tcp_client_socket_libevent.cc \
net/socket/tcp_listen_socket.cc \
net/socket/tcp_server_socket.cc \
+ net/socket/tcp_socket.cc \
net/socket/tcp_socket_libevent.cc \
net/socket/transport_client_socket_pool.cc \
net/socket/unix_domain_socket_posix.cc \
@@ -447,6 +451,8 @@ LOCAL_SRC_FILES := \
net/websockets/websocket_channel.cc \
net/websockets/websocket_deflater.cc \
net/websockets/websocket_errors.cc \
+ net/websockets/websocket_extension.cc \
+ net/websockets/websocket_extension_parser.cc \
net/websockets/websocket_frame.cc \
net/websockets/websocket_frame_parser.cc \
net/websockets/websocket_handshake_constants.cc \
@@ -494,7 +500,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -591,7 +597,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.darwin-arm.mk b/net/net_errors_java.target.darwin-arm.mk
index 987298e26c..107b79e346 100644
--- a/net/net_errors_java.target.darwin-arm.mk
+++ b/net/net_errors_java.target.darwin-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.darwin-mips.mk b/net/net_errors_java.target.darwin-mips.mk
index 53b7da1a8a..02ce8c1e14 100644
--- a/net/net_errors_java.target.darwin-mips.mk
+++ b/net/net_errors_java.target.darwin-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.darwin-x86.mk b/net/net_errors_java.target.darwin-x86.mk
index 0d84633890..5bd0c4d917 100644
--- a/net/net_errors_java.target.darwin-x86.mk
+++ b/net/net_errors_java.target.darwin-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.linux-arm.mk b/net/net_errors_java.target.linux-arm.mk
index 987298e26c..107b79e346 100644
--- a/net/net_errors_java.target.linux-arm.mk
+++ b/net/net_errors_java.target.linux-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.linux-mips.mk b/net/net_errors_java.target.linux-mips.mk
index 53b7da1a8a..02ce8c1e14 100644
--- a/net/net_errors_java.target.linux-mips.mk
+++ b/net/net_errors_java.target.linux-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_errors_java.target.linux-x86.mk b/net/net_errors_java.target.linux-x86.mk
index 0d84633890..5bd0c4d917 100644
--- a/net/net_errors_java.target.linux-x86.mk
+++ b/net/net_errors_java.target.linux-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.darwin-arm.mk b/net/net_jni_headers.target.darwin-arm.mk
index f0be7a3924..16301bab70 100644
--- a/net/net_jni_headers.target.darwin-arm.mk
+++ b/net/net_jni_headers.target.darwin-arm.mk
@@ -126,7 +126,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -206,7 +206,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.darwin-mips.mk b/net/net_jni_headers.target.darwin-mips.mk
index 45f5b82065..b410703875 100644
--- a/net/net_jni_headers.target.darwin-mips.mk
+++ b/net/net_jni_headers.target.darwin-mips.mk
@@ -125,7 +125,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -204,7 +204,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.darwin-x86.mk b/net/net_jni_headers.target.darwin-x86.mk
index d8ada7afe7..f50b55eff2 100644
--- a/net/net_jni_headers.target.darwin-x86.mk
+++ b/net/net_jni_headers.target.darwin-x86.mk
@@ -128,7 +128,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -211,7 +211,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.linux-arm.mk b/net/net_jni_headers.target.linux-arm.mk
index f0be7a3924..16301bab70 100644
--- a/net/net_jni_headers.target.linux-arm.mk
+++ b/net/net_jni_headers.target.linux-arm.mk
@@ -126,7 +126,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -206,7 +206,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.linux-mips.mk b/net/net_jni_headers.target.linux-mips.mk
index 45f5b82065..b410703875 100644
--- a/net/net_jni_headers.target.linux-mips.mk
+++ b/net/net_jni_headers.target.linux-mips.mk
@@ -125,7 +125,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -204,7 +204,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/net_jni_headers.target.linux-x86.mk b/net/net_jni_headers.target.linux-x86.mk
index d8ada7afe7..f50b55eff2 100644
--- a/net/net_jni_headers.target.linux-x86.mk
+++ b/net/net_jni_headers.target.linux-x86.mk
@@ -128,7 +128,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -211,7 +211,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.darwin-arm.mk b/net/private_key_types_java.target.darwin-arm.mk
index 0b5ee03d75..de9863a6f3 100644
--- a/net/private_key_types_java.target.darwin-arm.mk
+++ b/net/private_key_types_java.target.darwin-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.darwin-mips.mk b/net/private_key_types_java.target.darwin-mips.mk
index 0767d5f707..afcf3f6676 100644
--- a/net/private_key_types_java.target.darwin-mips.mk
+++ b/net/private_key_types_java.target.darwin-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.darwin-x86.mk b/net/private_key_types_java.target.darwin-x86.mk
index 4ca791d4fa..5039c8c46c 100644
--- a/net/private_key_types_java.target.darwin-x86.mk
+++ b/net/private_key_types_java.target.darwin-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.linux-arm.mk b/net/private_key_types_java.target.linux-arm.mk
index 0b5ee03d75..de9863a6f3 100644
--- a/net/private_key_types_java.target.linux-arm.mk
+++ b/net/private_key_types_java.target.linux-arm.mk
@@ -77,7 +77,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -157,7 +157,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.linux-mips.mk b/net/private_key_types_java.target.linux-mips.mk
index 0767d5f707..afcf3f6676 100644
--- a/net/private_key_types_java.target.linux-mips.mk
+++ b/net/private_key_types_java.target.linux-mips.mk
@@ -76,7 +76,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -155,7 +155,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/private_key_types_java.target.linux-x86.mk b/net/private_key_types_java.target.linux-x86.mk
index 4ca791d4fa..5039c8c46c 100644
--- a/net/private_key_types_java.target.linux-x86.mk
+++ b/net/private_key_types_java.target.linux-x86.mk
@@ -79,7 +79,7 @@ MY_CFLAGS_Debug := \
MY_DEFS_Debug := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
@@ -162,7 +162,7 @@ MY_CFLAGS_Release := \
MY_DEFS_Release := \
'-DANGLE_DX11' \
- '-DWTF_VECTOR_INITIAL_SIZE=16' \
+ '-DWTF_VECTOR_INITIAL_SIZE=4' \
'-D_FILE_OFFSET_BITS=64' \
'-DNO_TCMALLOC' \
'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
index 1630d9e378..12ffd1bd91 100644
--- a/net/proxy/proxy_resolver_perftest.cc
+++ b/net/proxy/proxy_resolver_perftest.cc
@@ -7,7 +7,7 @@
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
-#include "base/test/perftimer.h"
+#include "base/test/perf_time_logger.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/proxy/proxy_info.h"
@@ -130,7 +130,7 @@ class PacPerfSuiteRunner {
// Start the perf timer.
std::string perf_test_name = resolver_name_ + "_" + script_name;
- PerfTimeLogger timer(perf_test_name.c_str());
+ base::PerfTimeLogger timer(perf_test_name.c_str());
for (int i = 0; i < kNumIterations; ++i) {
// Round-robin between URLs to resolve.
diff --git a/net/proxy/proxy_script_decider.cc b/net/proxy/proxy_script_decider.cc
index 38bf751cd4..f6316a0215 100644
--- a/net/proxy/proxy_script_decider.cc
+++ b/net/proxy/proxy_script_decider.cc
@@ -9,6 +9,7 @@
#include "base/compiler_specific.h"
#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
@@ -16,6 +17,7 @@
#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
#include "net/proxy/proxy_script_fetcher.h"
+#include "net/url_request/url_request_context.h"
namespace net {
@@ -45,7 +47,10 @@ bool LooksLikePacScript(const base::string16& script) {
//
// For more details, also check out this comment:
// http://code.google.com/p/chromium/issues/detail?id=18575#c20
-static const char kWpadUrl[] = "http://wpad/wpad.dat";
+namespace {
+const char kWpadUrl[] = "http://wpad/wpad.dat";
+const int kQuickCheckDelayMs = 1000;
+};
base::Value* ProxyScriptDecider::PacSource::NetLogCallback(
const GURL* effective_pac_url,
@@ -82,6 +87,12 @@ ProxyScriptDecider::ProxyScriptDecider(
net_log_(BoundNetLog::Make(
net_log, NetLog::SOURCE_PROXY_SCRIPT_DECIDER)),
fetch_pac_bytes_(false) {
+ if (proxy_script_fetcher &&
+ proxy_script_fetcher->GetRequestContext() &&
+ proxy_script_fetcher->GetRequestContext()->host_resolver()) {
+ host_resolver_.reset(new SingleRequestHostResolver(
+ proxy_script_fetcher->GetRequestContext()->host_resolver()));
+ }
}
ProxyScriptDecider::~ProxyScriptDecider() {
@@ -106,6 +117,7 @@ int ProxyScriptDecider::Start(
wait_delay_ = base::TimeDelta();
pac_mandatory_ = config.pac_mandatory();
+ have_custom_pac_url_ = config.has_pac_url();
pac_sources_ = BuildPacSourcesFallbackList(config);
DCHECK(!pac_sources_.empty());
@@ -172,6 +184,13 @@ int ProxyScriptDecider::DoLoop(int result) {
case STATE_WAIT_COMPLETE:
rv = DoWaitComplete(rv);
break;
+ case STATE_QUICK_CHECK:
+ DCHECK_EQ(OK, rv);
+ rv = DoQuickCheck();
+ break;
+ case STATE_QUICK_CHECK_COMPLETE:
+ rv = DoQuickCheckComplete(rv);
+ break;
case STATE_FETCH_PAC_SCRIPT:
DCHECK_EQ(OK, rv);
rv = DoFetchPacScript();
@@ -221,10 +240,64 @@ int ProxyScriptDecider::DoWaitComplete(int result) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT,
result);
}
- next_state_ = GetStartState();
+ next_state_ = STATE_QUICK_CHECK;
return OK;
}
+int ProxyScriptDecider::DoQuickCheck() {
+ if (host_resolver_.get() == NULL) {
+ // If we have no resolver, skip QuickCheck altogether.
+ next_state_ = GetStartState();
+ return OK;
+ }
+
+ if (have_custom_pac_url_) {
+ // If there's a custom URL, skip QuickCheck.
+ next_state_ = GetStartState();
+ return OK;
+ }
+
+ quick_check_start_time_ = base::Time::Now();
+ HostResolver::RequestInfo reqinfo(HostPortPair("wpad", 80));
+ reqinfo.set_host_resolver_flags(HOST_RESOLVER_SYSTEM_ONLY);
+ CompletionCallback callback = base::Bind(
+ &ProxyScriptDecider::OnIOCompletion,
+ base::Unretained(this));
+
+
+ // We use HIGHEST here because proxy decision blocks doing any other requests.
+ int rv = host_resolver_->Resolve(reqinfo, HIGHEST, &wpad_addresses_,
+ callback, net_log_);
+
+ // We can't get an error response - the name is known to be valid, and we
+ // don't cache negative dns responses.
+ DCHECK(rv == OK || rv == ERR_IO_PENDING);
+
+ if (rv == OK) {
+ next_state_ = GetStartState();
+ } else {
+ quick_check_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(
+ kQuickCheckDelayMs),
+ base::Bind(callback, ERR_NAME_NOT_RESOLVED));
+ next_state_ = STATE_QUICK_CHECK_COMPLETE;
+ }
+ return rv;
+}
+
+int ProxyScriptDecider::DoQuickCheckComplete(int result) {
+ base::TimeDelta delta = base::Time::Now() - quick_check_start_time_;
+ if (result == OK)
+ UMA_HISTOGRAM_TIMES("Net.WpadQuickCheckSuccess", delta);
+ else
+ UMA_HISTOGRAM_TIMES("Net.WpadQuickCheckFailure", delta);
+ host_resolver_->Cancel();
+ quick_check_timer_.Stop();
+ if (result == OK)
+ next_state_ = GetStartState();
+ return result;
+}
+
int ProxyScriptDecider::DoFetchPacScript() {
DCHECK(fetch_pac_bytes_);
diff --git a/net/proxy/proxy_script_decider.h b/net/proxy/proxy_script_decider.h
index 9a77938ec8..23fa7af85c 100644
--- a/net/proxy/proxy_script_decider.h
+++ b/net/proxy/proxy_script_decider.h
@@ -12,9 +12,12 @@
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/single_request_host_resolver.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_resolver.h"
#include "url/gurl.h"
@@ -105,6 +108,8 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
STATE_NONE,
STATE_WAIT,
STATE_WAIT_COMPLETE,
+ STATE_QUICK_CHECK,
+ STATE_QUICK_CHECK_COMPLETE,
STATE_FETCH_PAC_SCRIPT,
STATE_FETCH_PAC_SCRIPT_COMPLETE,
STATE_VERIFY_PAC_SCRIPT,
@@ -121,6 +126,9 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
int DoWait();
int DoWaitComplete(int result);
+ int DoQuickCheck();
+ int DoQuickCheckComplete(int result);
+
int DoFetchPacScript();
int DoFetchPacScriptComplete(int result);
@@ -161,6 +169,9 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
// (i.e. fallback to direct connections are prohibited).
bool pac_mandatory_;
+ // Whether we have an existing custom PAC URL.
+ bool have_custom_pac_url_;
+
PacSourceList pac_sources_;
State next_state_;
@@ -175,6 +186,10 @@ class NET_EXPORT_PRIVATE ProxyScriptDecider {
ProxyConfig effective_config_;
scoped_refptr<ProxyResolverScriptData> script_data_;
+ AddressList wpad_addresses_;
+ base::OneShotTimer<ProxyScriptDecider> quick_check_timer_;
+ scoped_ptr<SingleRequestHostResolver> host_resolver_;
+ base::Time quick_check_start_time_;
DISALLOW_COPY_AND_ASSIGN(ProxyScriptDecider);
};
diff --git a/net/proxy/proxy_script_decider_unittest.cc b/net/proxy/proxy_script_decider_unittest.cc
index 977bd4df64..61978ab6e6 100644
--- a/net/proxy/proxy_script_decider_unittest.cc
+++ b/net/proxy/proxy_script_decider_unittest.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
@@ -14,11 +15,13 @@
#include "net/base/net_log.h"
#include "net/base/net_log_unittest.h"
#include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_decider.h"
#include "net/proxy/proxy_script_fetcher.h"
+#include "net/url_request/url_request_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -93,7 +96,12 @@ class Rules {
class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
public:
- explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
+ explicit RuleBasedProxyScriptFetcher(const Rules* rules)
+ : rules_(rules), request_context_(NULL) {}
+
+ virtual void SetRequestContext(URLRequestContext* context) {
+ request_context_ = context;
+ }
// ProxyScriptFetcher implementation.
virtual int Fetch(const GURL& url,
@@ -109,10 +117,13 @@ class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
virtual void Cancel() OVERRIDE {}
- virtual URLRequestContext* GetRequestContext() const OVERRIDE { return NULL; }
+ virtual URLRequestContext* GetRequestContext() const OVERRIDE {
+ return request_context_;
+ }
private:
const Rules* rules_;
+ URLRequestContext* request_context_;
};
// Succeed using custom PAC script.
@@ -243,6 +254,91 @@ TEST(ProxyScriptDeciderTest, AutodetectSuccess) {
EXPECT_EQ(rule.url, decider.effective_config().pac_url());
}
+class ProxyScriptDeciderQuickCheckTest : public ::testing::Test {
+ public:
+ ProxyScriptDeciderQuickCheckTest()
+ : rule_(rules_.AddSuccessRule("http://wpad/wpad.dat")),
+ fetcher_(&rules_) { }
+
+ virtual void SetUp() OVERRIDE {
+ request_context_.set_host_resolver(&resolver_);
+ fetcher_.SetRequestContext(&request_context_);
+ config_.set_auto_detect(true);
+ decider_.reset(new ProxyScriptDecider(&fetcher_, &dhcp_fetcher_, NULL));
+ }
+
+ int StartDecider() {
+ return decider_->Start(config_, base::TimeDelta(), true,
+ callback_.callback());
+ }
+
+ protected:
+ scoped_ptr<ProxyScriptDecider> decider_;
+ MockHostResolver resolver_;
+ Rules rules_;
+ Rules::Rule rule_;
+ TestCompletionCallback callback_;
+
+ private:
+ URLRequestContext request_context_;
+
+ RuleBasedProxyScriptFetcher fetcher_;
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher_;
+
+ ProxyConfig config_;
+};
+
+// Fails if a synchronous DNS lookup success for wpad causes QuickCheck to fail.
+TEST_F(ProxyScriptDeciderQuickCheckTest, SyncSuccess) {
+ resolver_.set_synchronous_mode(true);
+ resolver_.rules()->AddRule("wpad", "1.2.3.4");
+
+ EXPECT_EQ(OK, StartDecider());
+ EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
+
+ EXPECT_TRUE(decider_->effective_config().has_pac_url());
+ EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
+}
+
+// Fails if an asynchronous DNS lookup success for wpad causes QuickCheck to
+// fail.
+TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncSuccess) {
+ resolver_.set_ondemand_mode(true);
+ resolver_.rules()->AddRule("wpad", "1.2.3.4");
+
+ EXPECT_EQ(ERR_IO_PENDING, StartDecider());
+ ASSERT_TRUE(resolver_.has_pending_requests());
+ resolver_.ResolveAllPending();
+ callback_.WaitForResult();
+ EXPECT_FALSE(resolver_.has_pending_requests());
+ EXPECT_EQ(rule_.text(), decider_->script_data()->utf16());
+ EXPECT_TRUE(decider_->effective_config().has_pac_url());
+ EXPECT_EQ(rule_.url, decider_->effective_config().pac_url());
+}
+
+// Fails if an asynchronous DNS lookup failure (i.e. an NXDOMAIN) still causes
+// ProxyScriptDecider to yield a PAC URL.
+TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncFail) {
+ resolver_.set_ondemand_mode(true);
+ resolver_.rules()->AddSimulatedFailure("wpad");
+ EXPECT_EQ(ERR_IO_PENDING, StartDecider());
+ ASSERT_TRUE(resolver_.has_pending_requests());
+ resolver_.ResolveAllPending();
+ callback_.WaitForResult();
+ EXPECT_FALSE(decider_->effective_config().has_pac_url());
+}
+
+// Fails if a DNS lookup timeout either causes ProxyScriptDecider to yield a PAC
+// URL or causes ProxyScriptDecider not to cancel its pending resolution.
+TEST_F(ProxyScriptDeciderQuickCheckTest, AsyncTimeout) {
+ resolver_.set_ondemand_mode(true);
+ EXPECT_EQ(ERR_IO_PENDING, StartDecider());
+ ASSERT_TRUE(resolver_.has_pending_requests());
+ callback_.WaitForResult();
+ EXPECT_FALSE(resolver_.has_pending_requests());
+ EXPECT_FALSE(decider_->effective_config().has_pac_url());
+}
+
// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
TEST(ProxyScriptDeciderTest, AutodetectFailCustomSuccess1) {
Rules rules;
diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc
index 22f759fa03..99aa10fabf 100644
--- a/net/quic/congestion_control/fix_rate_sender.cc
+++ b/net/quic/congestion_control/fix_rate_sender.cc
@@ -60,15 +60,18 @@ void FixRateSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) {
// Ignore losses for fix rate sender.
}
-void FixRateSender::SentPacket(QuicTime sent_time,
- QuicPacketSequenceNumber /*sequence_number*/,
- QuicByteCount bytes,
- Retransmission is_retransmission) {
+bool FixRateSender::SentPacket(
+ QuicTime sent_time,
+ QuicPacketSequenceNumber /*sequence_number*/,
+ QuicByteCount bytes,
+ Retransmission is_retransmission,
+ HasRetransmittableData /*has_retransmittable_data*/) {
fix_rate_leaky_bucket_.Add(sent_time, bytes);
paced_sender_.SentPacket(sent_time, bytes);
if (is_retransmission == NOT_RETRANSMISSION) {
data_in_flight_ += bytes;
}
+ return true;
}
void FixRateSender::AbandoningPacket(
diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h
index 38cebad165..781deade1c 100644
--- a/net/quic/congestion_control/fix_rate_sender.h
+++ b/net/quic/congestion_control/fix_rate_sender.h
@@ -32,10 +32,12 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface {
QuicByteCount acked_bytes,
QuicTime::Delta rtt) OVERRIDE;
virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
- virtual void SentPacket(QuicTime sent_time,
- QuicPacketSequenceNumber equence_number,
- QuicByteCount bytes,
- Retransmission is_retransmission) OVERRIDE;
+ virtual bool SentPacket(
+ QuicTime sent_time,
+ QuicPacketSequenceNumber equence_number,
+ QuicByteCount bytes,
+ Retransmission is_retransmission,
+ HasRetransmittableData has_retransmittable_data) OVERRIDE;
virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number,
QuicByteCount abandoned_bytes) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc
index f914ed671a..e316f6517a 100644
--- a/net/quic/congestion_control/fix_rate_test.cc
+++ b/net/quic/congestion_control/fix_rate_test.cc
@@ -63,11 +63,14 @@ TEST_F(FixRateTest, SenderAPI) {
EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond());
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION);
+ sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION);
- sender_->SentPacket(clock_.Now(), 3, 600, NOT_RETRANSMISSION);
+ sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ sender_->SentPacket(clock_.Now(), 3, 600, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
sender_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
@@ -98,12 +101,12 @@ TEST_F(FixRateTest, FixRatePacing) {
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
NOT_HANDSHAKE).IsZero());
sender_->SentPacket(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION);
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
NOT_HANDSHAKE).IsZero());
sender_->SentPacket(clock_.Now(), sequence_number++, packet_size,
- NOT_RETRANSMISSION);
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
clock_.AdvanceTime(advance_time);
diff --git a/net/quic/congestion_control/inter_arrival_sender.cc b/net/quic/congestion_control/inter_arrival_sender.cc
index 3afa378529..5640a731ae 100644
--- a/net/quic/congestion_control/inter_arrival_sender.cc
+++ b/net/quic/congestion_control/inter_arrival_sender.cc
@@ -235,14 +235,17 @@ void InterArrivalSender::OnIncomingLoss(QuicTime ack_receive_time) {
}
}
-void InterArrivalSender::SentPacket(QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- Retransmission /*retransmit*/) {
+bool InterArrivalSender::SentPacket(
+ QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission /*is_retransmit*/,
+ HasRetransmittableData /*has_retransmittable_data*/) {
if (probing_) {
probe_->OnSentPacket(bytes);
}
paced_sender_->SentPacket(sent_time, bytes);
+ return true;
}
void InterArrivalSender::AbandoningPacket(
diff --git a/net/quic/congestion_control/inter_arrival_sender.h b/net/quic/congestion_control/inter_arrival_sender.h
index ad28ecd215..2c455cc9d1 100644
--- a/net/quic/congestion_control/inter_arrival_sender.h
+++ b/net/quic/congestion_control/inter_arrival_sender.h
@@ -43,10 +43,12 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface {
virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
- virtual void SentPacket(QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- Retransmission is_retransmit) OVERRIDE;
+ virtual bool SentPacket(
+ QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission is_retransmit,
+ HasRetransmittableData has_retransmittable_data) OVERRIDE;
virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number,
QuicByteCount abandoned_bytes) OVERRIDE;
diff --git a/net/quic/congestion_control/inter_arrival_sender_test.cc b/net/quic/congestion_control/inter_arrival_sender_test.cc
index d0faca0f8f..7392b1a3a6 100644
--- a/net/quic/congestion_control/inter_arrival_sender_test.cc
+++ b/net/quic/congestion_control/inter_arrival_sender_test.cc
@@ -41,7 +41,7 @@ class InterArrivalSenderTest : public ::testing::Test {
bytes_in_packet, send_clock_.Now());
sender_.SentPacket(send_clock_.Now(), sequence_number_, bytes_in_packet,
- NOT_RETRANSMISSION);
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
sequence_number_++;
}
EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(),
diff --git a/net/quic/congestion_control/quic_congestion_control_test.cc b/net/quic/congestion_control/quic_congestion_control_test.cc
index 0051acab1f..457538f455 100644
--- a/net/quic/congestion_control/quic_congestion_control_test.cc
+++ b/net/quic/congestion_control/quic_congestion_control_test.cc
@@ -49,7 +49,8 @@ TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) {
clock_.Now());
EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ manager_->SentPacket(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40),
manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE));
@@ -78,7 +79,8 @@ TEST_F(QuicCongestionControlTest, FixedRatePacing) {
for (QuicPacketSequenceNumber i = 1; i <= 100; ++i) {
EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ manager_->SentPacket(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
clock_.AdvanceTime(advance_time);
@@ -108,10 +110,12 @@ TEST_F(QuicCongestionControlTest, Pacing) {
for (QuicPacketSequenceNumber i = 1; i <= 100;) {
EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(),
NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE);
clock_.AdvanceTime(advance_time);
diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc
index ba6bab83ba..ec519db7c9 100644
--- a/net/quic/congestion_control/quic_congestion_manager.cc
+++ b/net/quic/congestion_control/quic_congestion_manager.cc
@@ -48,18 +48,21 @@ QuicCongestionManager::~QuicCongestionManager() {
STLDeleteValues(&packet_history_map_);
}
-void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number,
- QuicTime sent_time,
- QuicByteCount bytes,
- Retransmission retransmission) {
+void QuicCongestionManager::SentPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes,
+ Retransmission retransmission,
+ HasRetransmittableData has_retransmittable_data) {
DCHECK(!ContainsKey(pending_packets_, sequence_number));
- send_algorithm_->SentPacket(sent_time, sequence_number, bytes,
- retransmission);
- packet_history_map_[sequence_number] =
- new class SendAlgorithmInterface::SentPacket(bytes, sent_time);
- pending_packets_[sequence_number] = bytes;
- CleanupPacketHistory();
+ if (send_algorithm_->SentPacket(sent_time, sequence_number, bytes,
+ retransmission, has_retransmittable_data)) {
+ packet_history_map_[sequence_number] =
+ new class SendAlgorithmInterface::SentPacket(bytes, sent_time);
+ pending_packets_[sequence_number] = bytes;
+ CleanupPacketHistory();
+ }
}
// Called when a packet is timed out.
@@ -156,6 +159,23 @@ const QuicTime::Delta QuicCongestionManager::DefaultRetransmissionTime() {
return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
}
+// Ensures that the Delayed Ack timer is always set to a value lesser
+// than the retransmission timer's minimum value (MinRTO). We want the
+// delayed ack to get back to the QUIC peer before the sender's
+// retransmission timer triggers. Since we do not know the
+// reverse-path one-way delay, we assume equal delays for forward and
+// reverse paths, and ensure that the timer is set to less than half
+// of the MinRTO.
+// There may be a value in making this delay adaptive with the help of
+// the sender and a signaling mechanism -- if the sender uses a
+// different MinRTO, we may get spurious retransmissions. May not have
+// any benefits, but if the delayed ack becomes a significant source
+// of (likely, tail) latency, then consider such a mechanism.
+
+const QuicTime::Delta QuicCongestionManager::DelayedAckTime() {
+ return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
+}
+
const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay(
size_t unacked_packets_count,
size_t number_retransmissions) {
diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h
index 8bfa3c1d42..66303439ba 100644
--- a/net/quic/congestion_control/quic_congestion_manager.h
+++ b/net/quic/congestion_control/quic_congestion_manager.h
@@ -1,7 +1,3 @@
-// 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.
-
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -50,7 +46,8 @@ class NET_EXPORT_PRIVATE QuicCongestionManager {
virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
QuicTime sent_time,
QuicByteCount bytes,
- Retransmission retransmission);
+ Retransmission retransmission,
+ HasRetransmittableData has_retransmittable_data);
// Called when a packet is timed out.
virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number);
@@ -85,6 +82,9 @@ class NET_EXPORT_PRIVATE QuicCongestionManager {
const QuicTime::Delta DefaultRetransmissionTime();
+ // Returns amount of time for delayed ack timer.
+ const QuicTime::Delta DelayedAckTime();
+
const QuicTime::Delta GetRetransmissionDelay(
size_t unacked_packets_count,
size_t number_retransmissions);
diff --git a/net/quic/congestion_control/quic_congestion_manager_test.cc b/net/quic/congestion_control/quic_congestion_manager_test.cc
index 1cf44a2bdf..80460f552b 100644
--- a/net/quic/congestion_control/quic_congestion_manager_test.cc
+++ b/net/quic/congestion_control/quic_congestion_manager_test.cc
@@ -14,6 +14,7 @@
using testing::_;
using testing::StrictMock;
+using testing::Return;
namespace net {
namespace test {
@@ -64,7 +65,8 @@ TEST_F(QuicCongestionManagerTest, Bandwidth) {
clock_.AdvanceTime(advance_time);
EXPECT_TRUE(manager_->TimeUntilSend(
clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(i, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(i, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
// Ack the packet we sent.
ack.received_info.largest_observed = i;
manager_->OnIncomingAckFrame(ack, clock_.Now());
@@ -92,8 +94,8 @@ TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
EXPECT_TRUE(manager_->TimeUntilSend(
clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(
- sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000,
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
// Ack the packet we sent.
ack.received_info.largest_observed = sequence_number;
manager_->OnIncomingAckFrame(ack, clock_.Now());
@@ -118,7 +120,8 @@ TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) {
for (int i = 1; i <= 150; ++i) {
EXPECT_TRUE(manager_->TimeUntilSend(
clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero());
- manager_->SentPacket(i + 100, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(i + 100, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
// Ack the packet we sent.
ack.received_info.largest_observed = i + 100;
@@ -141,11 +144,13 @@ TEST_F(QuicCongestionManagerTest, Rtt) {
QuicPacketSequenceNumber sequence_number = 1;
QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
- EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _, _))
+ .Times(1).WillOnce(Return(true));
EXPECT_CALL(*send_algorithm,
OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
- manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
QuicAckFrame ack;
@@ -167,11 +172,13 @@ TEST_F(QuicCongestionManagerTest, RttWithInvalidDelta) {
QuicPacketSequenceNumber sequence_number = 1;
QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite();
- EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _, _))
+ .Times(1).WillOnce(Return(true));
EXPECT_CALL(*send_algorithm,
OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
- manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
QuicAckFrame ack;
@@ -193,11 +200,13 @@ TEST_F(QuicCongestionManagerTest, RttInfiniteDelta) {
QuicPacketSequenceNumber sequence_number = 1;
QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite();
- EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _, _))
+ .Times(1).WillOnce(Return(true));
EXPECT_CALL(*send_algorithm,
OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
- manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
QuicAckFrame ack;
@@ -218,11 +227,13 @@ TEST_F(QuicCongestionManagerTest, RttZeroDelta) {
QuicPacketSequenceNumber sequence_number = 1;
QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
- EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
- EXPECT_CALL(*send_algorithm,
- OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _, _))
+ .Times(1).WillOnce(Return(true));
+ EXPECT_CALL(*send_algorithm, OnIncomingAck(sequence_number, _, expected_rtt))
+ .Times(1);
- manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
clock_.AdvanceTime(expected_rtt);
QuicAckFrame ack;
diff --git a/net/quic/congestion_control/send_algorithm_interface.cc b/net/quic/congestion_control/send_algorithm_interface.cc
index ce24a00b1a..90399e7d05 100644
--- a/net/quic/congestion_control/send_algorithm_interface.cc
+++ b/net/quic/congestion_control/send_algorithm_interface.cc
@@ -14,7 +14,7 @@ const bool kUseReno = false;
// TODO(ianswett): Increase the max congestion window once the RTO logic is
// improved, particularly in cases when RTT is larger than the RTO. b/10075719
// Maximum number of outstanding packets for tcp.
-const QuicTcpCongestionWindow kMaxTcpCongestionWindow = 50;
+const QuicTcpCongestionWindow kMaxTcpCongestionWindow = 100;
// Factory for send side congestion control algorithm.
SendAlgorithmInterface* SendAlgorithmInterface::Create(
diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h
index 8896b2b06d..c29f22545d 100644
--- a/net/quic/congestion_control/send_algorithm_interface.h
+++ b/net/quic/congestion_control/send_algorithm_interface.h
@@ -55,11 +55,15 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface {
virtual void OnIncomingLoss(QuicTime ack_receive_time) = 0;
// Inform that we sent x bytes to the wire, and if that was a retransmission.
+ // Returns true if the packet should be tracked by the congestion manager,
+ // false otherwise. This is used by implementations such as tcp_cubic_sender
+ // that do not count outgoing ACK packets against the congestion window.
// Note: this function must be called for every packet sent to the wire.
- virtual void SentPacket(QuicTime sent_time,
+ virtual bool SentPacket(QuicTime sent_time,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- Retransmission is_retransmission) = 0;
+ Retransmission is_retransmission,
+ HasRetransmittableData is_retransmittable) = 0;
// Called when a packet is timed out.
virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number,
diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc
index 438dbe912e..52c910e859 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -65,6 +65,7 @@ void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
void TcpCubicSender::OnIncomingAck(
QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes,
QuicTime::Delta rtt) {
+ DCHECK_GE(bytes_in_flight_, acked_bytes);
bytes_in_flight_ -= acked_bytes;
CongestionAvoidance(acked_sequence_number);
AckAccounting(rtt);
@@ -93,10 +94,16 @@ void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) {
DLOG(INFO) << "Incoming loss; congestion window:" << congestion_window_;
}
-void TcpCubicSender::SentPacket(QuicTime /*sent_time*/,
+bool TcpCubicSender::SentPacket(QuicTime /*sent_time*/,
QuicPacketSequenceNumber sequence_number,
QuicByteCount bytes,
- Retransmission is_retransmission) {
+ Retransmission is_retransmission,
+ HasRetransmittableData is_retransmittable) {
+ // Only update bytes_in_flight_ for data packets.
+ if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
+ return false;
+ }
+
bytes_in_flight_ += bytes;
if (is_retransmission == NOT_RETRANSMISSION && update_end_sequence_number_) {
end_sequence_number_ = sequence_number;
@@ -105,10 +112,12 @@ void TcpCubicSender::SentPacket(QuicTime /*sent_time*/,
DLOG(INFO) << "Stop update end sequence number @" << sequence_number;
}
}
+ return true;
}
void TcpCubicSender::AbandoningPacket(QuicPacketSequenceNumber sequence_number,
QuicByteCount abandoned_bytes) {
+ DCHECK_GE(bytes_in_flight_, abandoned_bytes);
bytes_in_flight_ -= abandoned_bytes;
}
diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h
index 8cea0aa44e..db829c29bb 100644
--- a/net/quic/congestion_control/tcp_cubic_sender.h
+++ b/net/quic/congestion_control/tcp_cubic_sender.h
@@ -41,10 +41,12 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
QuicByteCount acked_bytes,
QuicTime::Delta rtt) OVERRIDE;
virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
- virtual void SentPacket(QuicTime sent_time,
- QuicPacketSequenceNumber sequence_number,
- QuicByteCount bytes,
- Retransmission is_retransmission) OVERRIDE;
+ virtual bool SentPacket(
+ QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission is_retransmission,
+ HasRetransmittableData is_retransmittable) OVERRIDE;
virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number,
QuicByteCount abandoned_bytes) OVERRIDE;
virtual QuicTime::Delta TimeUntilSend(
diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc
index fb67fcd2f5..c7046fcb2f 100644
--- a/net/quic/congestion_control/tcp_cubic_sender_test.cc
+++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -45,7 +45,7 @@ class TcpCubicSenderTest : public ::testing::Test {
while (bytes_to_send > 0) {
QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send);
sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet,
- NOT_RETRANSMISSION);
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
bytes_to_send -= bytes_in_packet;
if (bytes_to_send > 0) {
EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
@@ -352,5 +352,22 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
}
+TEST_F(TcpCubicSenderTest, CongestionWindowNotAffectedByAcks) {
+ QuicByteCount congestion_window = sender_->AvailableCongestionWindow();
+
+ // Send a packet with no retransmittable data, and ensure that the congestion
+ // window doesn't change.
+ QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, congestion_window);
+ sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet,
+ NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
+ EXPECT_EQ(congestion_window, sender_->AvailableCongestionWindow());
+
+ // Send a data packet with retransmittable data, and ensure that the
+ // congestion window has shrunk.
+ sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet,
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ EXPECT_GT(congestion_window, sender_->AvailableCongestionWindow());
+}
+
} // namespace test
} // namespace net
diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc
index c5caa8c838..89cea42862 100644
--- a/net/quic/crypto/crypto_server_config.cc
+++ b/net/quic/crypto/crypto_server_config.cc
@@ -306,7 +306,6 @@ struct ClientHelloInfo {
QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
const CryptoHandshakeMessage& client_hello,
- QuicVersion version,
QuicGuid guid,
const IPEndPoint& client_ip,
const QuicClock* clock,
@@ -361,8 +360,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
!info.client_nonce_well_formed ||
!info.unique ||
!requested_config.get()) {
- BuildRejection(version, primary_config.get(), client_hello, info, rand,
- out);
+ BuildRejection(primary_config.get(), client_hello, info, rand, out);
return QUIC_NO_ERROR;
}
@@ -668,7 +666,6 @@ QuicErrorCode QuicCryptoServerConfig::EvaluateClientHello(
}
void QuicCryptoServerConfig::BuildRejection(
- QuicVersion version,
const scoped_refptr<Config>& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
@@ -712,9 +709,8 @@ void QuicCryptoServerConfig::BuildRejection(
const vector<string>* certs;
string signature;
- if (!proof_source_->GetProof(version, info.sni.as_string(),
- config->serialized, x509_ecdsa_supported,
- &certs, &signature)) {
+ if (!proof_source_->GetProof(info.sni.as_string(), config->serialized,
+ x509_ecdsa_supported, &certs, &signature)) {
return;
}
diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h
index cc40cb0d34..4255d22861 100644
--- a/net/quic/crypto/crypto_server_config.h
+++ b/net/quic/crypto/crypto_server_config.h
@@ -116,8 +116,6 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// an error code is returned.
//
// client_hello: the incoming client hello message.
- // version: the QUIC version for the connection. TODO(wtc): Remove once
- // QUIC_VERSION_7 and before are removed.
// guid: the GUID for the connection, which is used in key derivation.
// client_ip: the IP address of the client, which is used to generate and
// validate source-address tokens.
@@ -129,7 +127,6 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// out: the resulting handshake message (either REJ or SHLO)
// error_details: used to store a string describing any error.
QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello,
- QuicVersion version,
QuicGuid guid,
const IPEndPoint& client_ip,
const QuicClock* clock,
@@ -266,7 +263,6 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
// BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
void BuildRejection(
- QuicVersion version,
const scoped_refptr<Config>& config,
const CryptoHandshakeMessage& client_hello,
const ClientHelloInfo& info,
diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc
index 6744d12e5e..b2cdf820c3 100644
--- a/net/quic/crypto/crypto_server_test.cc
+++ b/net/quic/crypto/crypto_server_test.cc
@@ -72,8 +72,8 @@ class CryptoServerTest : public ::testing::Test {
void ShouldSucceed(const CryptoHandshakeMessage& message) {
string error_details;
QuicErrorCode error = config_.ProcessClientHello(
- message, QuicVersionMax(), 1 /* GUID */, addr_,
- &clock_, rand_, &params_, &out_, &error_details);
+ message, 1 /* GUID */, addr_, &clock_,
+ rand_, &params_, &out_, &error_details);
ASSERT_EQ(error, QUIC_NO_ERROR)
<< "Message failed with error " << error_details << ": "
@@ -84,8 +84,8 @@ class CryptoServerTest : public ::testing::Test {
const CryptoHandshakeMessage& message) {
string error_details;
QuicErrorCode error = config_.ProcessClientHello(
- message, QuicVersionMax(), 1 /* GUID */, addr_,
- &clock_, rand_, &params_, &out_, &error_details);
+ message, 1 /* GUID */, addr_, &clock_,
+ rand_, &params_, &out_, &error_details);
ASSERT_NE(error, QUIC_NO_ERROR)
<< "Message didn't fail: " << message.DebugString();
diff --git a/net/quic/crypto/proof_source.h b/net/quic/crypto/proof_source.h
index ba5087b0f6..4482dd9956 100644
--- a/net/quic/crypto/proof_source.h
+++ b/net/quic/crypto/proof_source.h
@@ -9,7 +9,6 @@
#include <vector>
#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
namespace net {
@@ -28,9 +27,6 @@ class NET_EXPORT_PRIVATE ProofSource {
//
// The signature uses SHA-256 as the hash function when the key is ECDSA.
//
- // |version| is the QUIC version for the connection. TODO(wtc): Remove once
- // QUIC_VERSION_7 and before are removed.
- //
// If |ecdsa_ok| is true, the signature may use an ECDSA key. Otherwise, the
// signature must use an RSA key.
//
@@ -49,8 +45,7 @@ class NET_EXPORT_PRIVATE ProofSource {
// used.
//
// This function may be called concurrently.
- virtual bool GetProof(QuicVersion version,
- const std::string& hostname,
+ virtual bool GetProof(const std::string& hostname,
const std::string& server_config,
bool ecdsa_ok,
const std::vector<std::string>** out_certs,
diff --git a/net/quic/crypto/proof_source_chromium.cc b/net/quic/crypto/proof_source_chromium.cc
index 4c1fe263b6..7522631381 100644
--- a/net/quic/crypto/proof_source_chromium.cc
+++ b/net/quic/crypto/proof_source_chromium.cc
@@ -12,8 +12,7 @@ namespace net {
ProofSourceChromium::ProofSourceChromium() {
}
-bool ProofSourceChromium::GetProof(QuicVersion version,
- const string& hostname,
+bool ProofSourceChromium::GetProof(const string& hostname,
const string& server_config,
bool ecdsa_ok,
const vector<string>** out_certs,
diff --git a/net/quic/crypto/proof_source_chromium.h b/net/quic/crypto/proof_source_chromium.h
index 2b93e2d9a4..70ab92d91c 100644
--- a/net/quic/crypto/proof_source_chromium.h
+++ b/net/quic/crypto/proof_source_chromium.h
@@ -23,8 +23,7 @@ class NET_EXPORT_PRIVATE ProofSourceChromium : public ProofSource {
virtual ~ProofSourceChromium() {}
// ProofSource interface
- virtual bool GetProof(QuicVersion version,
- const std::string& hostname,
+ virtual bool GetProof(const std::string& hostname,
const std::string& server_config,
bool ecdsa_ok,
const std::vector<std::string>** out_certs,
diff --git a/net/quic/crypto/proof_test.cc b/net/quic/crypto/proof_test.cc
index 97b0dcb4ea..e4e661a298 100644
--- a/net/quic/crypto/proof_test.cc
+++ b/net/quic/crypto/proof_test.cc
@@ -25,21 +25,7 @@ using std::vector;
namespace net {
namespace test {
-class ProofTest : public ::testing::TestWithParam<QuicVersion> {
- protected:
- ProofTest() {
- version_ = GetParam();
- }
-
- QuicVersion version_;
-};
-
-// Run all ProofTests with QUIC versions 7 and 8.
-INSTANTIATE_TEST_CASE_P(ProofTests,
- ProofTest,
- ::testing::Values(QUIC_VERSION_7, QUIC_VERSION_8));
-
-TEST_P(ProofTest, Verify) {
+TEST(ProofTest, Verify) {
// TODO(rtenneti): Enable testing of ProofVerifier.
#if 0
scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
@@ -53,11 +39,10 @@ TEST_P(ProofTest, Verify) {
string error_details, signature, first_signature;
CertVerifyResult cert_verify_result;
- ASSERT_TRUE(source->GetProof(version_, hostname, server_config,
- false /* no ECDSA */, &first_certs,
- &first_signature));
- ASSERT_TRUE(source->GetProof(version_, hostname, server_config,
- false /* no ECDSA */, &certs, &signature));
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &first_certs, &first_signature));
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &certs, &signature));
// Check that the proof source is caching correctly:
ASSERT_EQ(first_certs, certs);
@@ -65,23 +50,22 @@ TEST_P(ProofTest, Verify) {
int rv;
TestCompletionCallback callback;
- rv = verifier->VerifyProof(version_, hostname, server_config, *certs,
- signature, &error_details, &cert_verify_result,
+ rv = verifier->VerifyProof(hostname, server_config, *certs, signature,
+ &error_details, &cert_verify_result,
callback.callback());
rv = callback.GetResult(rv);
ASSERT_EQ(OK, rv);
ASSERT_EQ("", error_details);
ASSERT_FALSE(IsCertStatusError(cert_verify_result.cert_status));
- rv = verifier->VerifyProof(version_, "foo.com", server_config, *certs,
- signature, &error_details, &cert_verify_result,
+ rv = verifier->VerifyProof("foo.com", server_config, *certs, signature,
+ &error_details, &cert_verify_result,
callback.callback());
rv = callback.GetResult(rv);
ASSERT_EQ(ERR_FAILED, rv);
ASSERT_NE("", error_details);
- rv = verifier->VerifyProof(version_, hostname,
- server_config.substr(1, string::npos),
+ rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos),
*certs, signature, &error_details,
&cert_verify_result, callback.callback());
rv = callback.GetResult(rv);
@@ -89,7 +73,7 @@ TEST_P(ProofTest, Verify) {
ASSERT_NE("", error_details);
const string corrupt_signature = "1" + signature;
- rv = verifier->VerifyProof(version_, hostname, server_config, *certs,
+ rv = verifier->VerifyProof(hostname, server_config, *certs,
corrupt_signature, &error_details,
&cert_verify_result, callback.callback());
rv = callback.GetResult(rv);
@@ -100,8 +84,8 @@ TEST_P(ProofTest, Verify) {
for (size_t i = 1; i < certs->size(); i++) {
wrong_certs.push_back((*certs)[i]);
}
- rv = verifier->VerifyProof(version_, "foo.com", server_config, wrong_certs,
- signature, &error_details, &cert_verify_result,
+ rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature,
+ &error_details, &cert_verify_result,
callback.callback());
rv = callback.GetResult(rv);
ASSERT_EQ(ERR_FAILED, rv);
@@ -138,8 +122,7 @@ class TestProofVerifierCallback : public ProofVerifierCallback {
// RunVerification runs |verifier->VerifyProof| and asserts that the result
// matches |expected_ok|.
-static void RunVerification(QuicVersion version,
- ProofVerifier* verifier,
+static void RunVerification(ProofVerifier* verifier,
const std::string& hostname,
const std::string& server_config,
const vector<std::string>& certs,
@@ -153,7 +136,7 @@ static void RunVerification(QuicVersion version,
new TestProofVerifierCallback(&comp_callback, &ok, &error_details);
ProofVerifier::Status status = verifier->VerifyProof(
- version, hostname, server_config, certs, proof, &error_details, &details,
+ hostname, server_config, certs, proof, &error_details, &details,
callback);
switch (status) {
@@ -185,56 +168,11 @@ static string PEMCertFileToDER(const string& file_name) {
// A known answer test that allows us to test ProofVerifier without a working
// ProofSource.
-TEST_P(ProofTest, VerifyRSAKnownAnswerTest) {
+TEST(ProofTest, VerifyRSAKnownAnswerTest) {
// These sample signatures were generated by running the Proof.Verify test
// and dumping the bytes of the |signature| output of ProofSource::GetProof().
// sLen = special value -2 used by OpenSSL.
static const unsigned char signature_data_0[] = {
- 0x4c, 0x68, 0x3c, 0xc2, 0x1f, 0x31, 0x73, 0xa5, 0x29, 0xd3,
- 0x56, 0x75, 0xb1, 0xbf, 0xbd, 0x31, 0x17, 0xfb, 0x2e, 0x24,
- 0xb3, 0xc4, 0x0d, 0xfa, 0x56, 0xb8, 0x65, 0x94, 0x12, 0x38,
- 0x6e, 0xff, 0xb3, 0x10, 0x2e, 0xf8, 0x5c, 0xc1, 0x21, 0x9d,
- 0x29, 0x0c, 0x3a, 0x0a, 0x1a, 0xbf, 0x6b, 0x1c, 0x63, 0x77,
- 0xf7, 0x86, 0xd3, 0xa4, 0x36, 0xf2, 0xb1, 0x6f, 0xac, 0xc3,
- 0x23, 0x8d, 0xda, 0xe6, 0xd5, 0x83, 0xba, 0xdf, 0x28, 0x3e,
- 0x7f, 0x4e, 0x79, 0xfc, 0xba, 0xdb, 0xf7, 0xd0, 0x4b, 0xad,
- 0x79, 0xd0, 0xeb, 0xcf, 0xfa, 0x6e, 0x84, 0x44, 0x7a, 0x26,
- 0xb1, 0x29, 0xa3, 0x08, 0xa8, 0x63, 0xfd, 0xed, 0x85, 0xff,
- 0x9a, 0xe6, 0x79, 0x8b, 0xb6, 0x81, 0x13, 0x2c, 0xde, 0xe2,
- 0xd8, 0x31, 0x29, 0xa4, 0xe0, 0x1b, 0x75, 0x2d, 0x8a, 0xf8,
- 0x27, 0x55, 0xbc, 0xc7, 0x3b, 0x1e, 0xc1, 0x42,
- };
- static const unsigned char signature_data_1[] = {
- 0xbb, 0xd1, 0x17, 0x43, 0xf3, 0x42, 0x16, 0xe9, 0xf9, 0x76,
- 0xe6, 0xe3, 0xaa, 0x50, 0x47, 0x5f, 0x93, 0xb6, 0x7d, 0x35,
- 0x03, 0x49, 0x0a, 0x07, 0x61, 0xd5, 0xf1, 0x9c, 0x6b, 0xaf,
- 0xaa, 0xd7, 0x64, 0xe4, 0x0a, 0x0c, 0xab, 0x97, 0xfb, 0x4e,
- 0x5c, 0x14, 0x08, 0xf6, 0xb9, 0xa9, 0x1d, 0xa9, 0xf8, 0x6d,
- 0xb0, 0x2b, 0x2a, 0x0e, 0xc4, 0xd0, 0xd2, 0xe9, 0x96, 0x4f,
- 0x44, 0x70, 0x90, 0x46, 0xb9, 0xd5, 0x89, 0x72, 0xb9, 0xa8,
- 0xe4, 0xfb, 0x88, 0xbc, 0x69, 0x7f, 0xc9, 0xdc, 0x84, 0x87,
- 0x18, 0x21, 0x9b, 0xde, 0x22, 0x33, 0xde, 0x16, 0x3f, 0xe6,
- 0xfd, 0x27, 0x56, 0xd3, 0xa4, 0x97, 0x91, 0x65, 0x1a, 0xe7,
- 0x5e, 0x80, 0x9a, 0xbf, 0xbf, 0x1a, 0x29, 0x8a, 0xbe, 0xa2,
- 0x8c, 0x9c, 0x23, 0xf4, 0xcb, 0xba, 0x79, 0x31, 0x28, 0xab,
- 0x77, 0x94, 0x92, 0xb2, 0xc2, 0x35, 0xb2, 0xfa,
- };
- static const unsigned char signature_data_2[] = {
- 0x7e, 0x17, 0x01, 0xcb, 0x76, 0x9e, 0x9f, 0xce, 0xeb, 0x66,
- 0x3e, 0xaa, 0xc9, 0x36, 0x5b, 0x7e, 0x48, 0x25, 0x99, 0xf8,
- 0x0d, 0xe1, 0xa8, 0x48, 0x93, 0x3c, 0xe8, 0x97, 0x2e, 0x98,
- 0xd6, 0x73, 0x0f, 0xd0, 0x74, 0x9c, 0x17, 0xef, 0xee, 0xf8,
- 0x0e, 0x2a, 0x27, 0x3f, 0xc6, 0x55, 0xc6, 0xb9, 0xfe, 0x17,
- 0xcc, 0xeb, 0x5d, 0xa1, 0xdc, 0xbd, 0x64, 0xd9, 0x5e, 0xec,
- 0x57, 0x9d, 0xc3, 0xdc, 0x11, 0xbf, 0x23, 0x02, 0x58, 0xc4,
- 0xf1, 0x18, 0xc1, 0x6f, 0x3f, 0xef, 0x18, 0x4d, 0xa6, 0x1e,
- 0xe8, 0x25, 0x32, 0x8f, 0x92, 0x1e, 0xad, 0xbc, 0xbe, 0xde,
- 0x83, 0x2a, 0x92, 0xd5, 0x59, 0x6f, 0xe4, 0x95, 0x6f, 0xe6,
- 0xb1, 0xf9, 0xaf, 0x3f, 0xdb, 0x69, 0x6f, 0xae, 0xa6, 0x36,
- 0xd2, 0x50, 0x81, 0x78, 0x41, 0x13, 0x2c, 0x65, 0x9c, 0x9e,
- 0xf4, 0xd2, 0xd5, 0x58, 0x5b, 0x8b, 0x87, 0xcf,
- };
- static const unsigned char signature_data_4[] = {
0x9e, 0xe6, 0x74, 0x3b, 0x8f, 0xb8, 0x66, 0x77, 0x57, 0x09,
0x8a, 0x04, 0xe9, 0xf0, 0x7c, 0x91, 0xa9, 0x5c, 0xe9, 0xdf,
0x12, 0x4d, 0x23, 0x82, 0x8c, 0x29, 0x72, 0x7f, 0xc2, 0x20,
@@ -249,7 +187,7 @@ TEST_P(ProofTest, VerifyRSAKnownAnswerTest) {
0x78, 0xc8, 0x8b, 0xf5, 0xb9, 0x36, 0x5d, 0x72, 0x1f, 0xfc,
0x14, 0xff, 0xa7, 0x81, 0x27, 0x49, 0xae, 0xe1,
};
- static const unsigned char signature_data_5[] = {
+ static const unsigned char signature_data_1[] = {
0x5e, 0xc2, 0xab, 0x6b, 0x16, 0xe6, 0x55, 0xf3, 0x16, 0x46,
0x35, 0xdc, 0xcc, 0xde, 0xd0, 0xbd, 0x6c, 0x66, 0xb2, 0x3d,
0xd3, 0x14, 0x78, 0xed, 0x47, 0x55, 0xfb, 0xdb, 0xe1, 0x7d,
@@ -264,7 +202,7 @@ TEST_P(ProofTest, VerifyRSAKnownAnswerTest) {
0xaf, 0x6b, 0x47, 0xbc, 0x16, 0x55, 0x37, 0x0a, 0xbe, 0x0e,
0xc5, 0x75, 0x3f, 0x3d, 0x8e, 0xe8, 0x44, 0xe3,
};
- static const unsigned char signature_data_6[] = {
+ static const unsigned char signature_data_2[] = {
0x8e, 0x5c, 0x78, 0x63, 0x74, 0x99, 0x2e, 0x96, 0xc0, 0x14,
0x8d, 0xb5, 0x13, 0x74, 0xa3, 0xa4, 0xe0, 0x43, 0x3e, 0x85,
0xba, 0x8f, 0x3c, 0x5e, 0x14, 0x64, 0x0e, 0x5e, 0xff, 0x89,
@@ -295,52 +233,41 @@ TEST_P(ProofTest, VerifyRSAKnownAnswerTest) {
// Signatures are nondeterministic, so we test multiple signatures on the
// same server_config.
vector<string> signatures(3);
- if (version_ < QUIC_VERSION_8) {
- signatures[0].assign(reinterpret_cast<const char*>(signature_data_0),
- sizeof(signature_data_0));
- signatures[1].assign(reinterpret_cast<const char*>(signature_data_1),
- sizeof(signature_data_1));
- signatures[2].assign(reinterpret_cast<const char*>(signature_data_2),
- sizeof(signature_data_2));
- } else {
- signatures[0].assign(reinterpret_cast<const char*>(signature_data_4),
- sizeof(signature_data_4));
- signatures[1].assign(reinterpret_cast<const char*>(signature_data_5),
- sizeof(signature_data_5));
- signatures[2].assign(reinterpret_cast<const char*>(signature_data_6),
- sizeof(signature_data_6));
- }
+ signatures[0].assign(reinterpret_cast<const char*>(signature_data_0),
+ sizeof(signature_data_0));
+ signatures[1].assign(reinterpret_cast<const char*>(signature_data_1),
+ sizeof(signature_data_1));
+ signatures[2].assign(reinterpret_cast<const char*>(signature_data_2),
+ sizeof(signature_data_2));
for (size_t i = 0; i < signatures.size(); i++) {
const string& signature = signatures[i];
RunVerification(
- version_, verifier.get(), hostname, server_config, certs, signature,
- true);
+ verifier.get(), hostname, server_config, certs, signature, true);
RunVerification(
- version_, verifier.get(), "foo.com", server_config, certs, signature,
- false);
+ verifier.get(), "foo.com", server_config, certs, signature, false);
RunVerification(
- version_, verifier.get(), hostname,
- server_config.substr(1, string::npos), certs, signature, false);
+ verifier.get(), hostname, server_config.substr(1, string::npos),
+ certs, signature, false);
const string corrupt_signature = "1" + signature;
RunVerification(
- version_, verifier.get(), hostname, server_config, certs,
- corrupt_signature, false);
+ verifier.get(), hostname, server_config, certs, corrupt_signature,
+ false);
vector<string> wrong_certs;
for (size_t i = 1; i < certs.size(); i++) {
wrong_certs.push_back(certs[i]);
}
- RunVerification(version_, verifier.get(), hostname, server_config,
- wrong_certs, signature, false);
+ RunVerification(verifier.get(), hostname, server_config, wrong_certs,
+ signature, false);
}
}
// A known answer test that allows us to test ProofVerifier without a working
// ProofSource.
-TEST_P(ProofTest, VerifyECDSAKnownAnswerTest) {
+TEST(ProofTest, VerifyECDSAKnownAnswerTest) {
// Disable this test on platforms that do not support ECDSA certificates.
#if defined(OS_WIN)
if (base::win::GetVersion() < base::win::VERSION_VISTA)
@@ -406,36 +333,34 @@ TEST_P(ProofTest, VerifyECDSAKnownAnswerTest) {
const string& signature = signatures[i];
RunVerification(
- version_, verifier.get(), hostname, server_config, certs, signature,
- true);
+ verifier.get(), hostname, server_config, certs, signature, true);
RunVerification(
- version_, verifier.get(), "foo.com", server_config, certs, signature,
- false);
+ verifier.get(), "foo.com", server_config, certs, signature, false);
RunVerification(
- version_, verifier.get(), hostname,
- server_config.substr(1, string::npos), certs, signature, false);
+ verifier.get(), hostname, server_config.substr(1, string::npos),
+ certs, signature, false);
// An ECDSA signature is DER-encoded. Corrupt the last byte so that the
// signature can still be DER-decoded correctly.
string corrupt_signature = signature;
corrupt_signature[corrupt_signature.size() - 1] += 1;
RunVerification(
- version_, verifier.get(), hostname, server_config, certs,
- corrupt_signature, false);
+ verifier.get(), hostname, server_config, certs, corrupt_signature,
+ false);
// Prepending a "1" makes the DER invalid.
const string bad_der_signature1 = "1" + signature;
RunVerification(
- version_, verifier.get(), hostname, server_config, certs,
- bad_der_signature1, false);
+ verifier.get(), hostname, server_config, certs, bad_der_signature1,
+ false);
vector<string> wrong_certs;
for (size_t i = 1; i < certs.size(); i++) {
wrong_certs.push_back(certs[i]);
}
RunVerification(
- version_, verifier.get(), hostname, server_config, wrong_certs,
- signature, false);
+ verifier.get(), hostname, server_config, wrong_certs, signature,
+ false);
}
}
diff --git a/net/quic/crypto/proof_verifier.h b/net/quic/crypto/proof_verifier.h
index ecab113e69..f469c55295 100644
--- a/net/quic/crypto/proof_verifier.h
+++ b/net/quic/crypto/proof_verifier.h
@@ -10,7 +10,6 @@
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
-#include "net/quic/quic_protocol.h"
namespace net {
@@ -71,11 +70,7 @@ class NET_EXPORT_PRIVATE ProofVerifier {
//
// The signature uses SHA-256 as the hash function and PSS padding in the
// case of RSA.
- //
- // |version| is the QUIC version for the connection. TODO(wtc): Remove once
- // QUIC_VERSION_7 and before are removed.
- virtual Status VerifyProof(QuicVersion version,
- const std::string& hostname,
+ virtual Status VerifyProof(const std::string& hostname,
const std::string& server_config,
const std::vector<std::string>& certs,
const std::string& signature,
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index 88653053f3..8c4796204e 100644
--- a/net/quic/crypto/proof_verifier_chromium.cc
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -42,7 +42,6 @@ ProofVerifierChromium::~ProofVerifierChromium() {
}
ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
- QuicVersion version,
const string& hostname,
const string& server_config,
const vector<string>& certs,
@@ -90,7 +89,7 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof(
// We call VerifySignature first to avoid copying of server_config and
// signature.
- if (!VerifySignature(version, server_config, signature, certs[0])) {
+ if (!VerifySignature(server_config, signature, certs[0])) {
*error_details = "Failed to verify signature of server config";
DLOG(WARNING) << *error_details;
verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
@@ -177,8 +176,7 @@ int ProofVerifierChromium::DoVerifyCertComplete(int result) {
return result;
}
-bool ProofVerifierChromium::VerifySignature(QuicVersion version,
- const string& signed_data,
+bool ProofVerifierChromium::VerifySignature(const string& signed_data,
const string& signature,
const string& cert) {
StringPiece spki;
@@ -198,11 +196,9 @@ bool ProofVerifierChromium::VerifySignature(QuicVersion version,
crypto::SignatureVerifier::SHA256;
crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg;
unsigned int hash_len = 32; // 32 is the length of a SHA-256 hash.
- unsigned int salt_len =
- version >= QUIC_VERSION_8 ? hash_len : signature.size() - hash_len - 2;
bool ok = verifier.VerifyInitRSAPSS(
- hash_alg, mask_hash_alg, salt_len,
+ hash_alg, mask_hash_alg, hash_len,
reinterpret_cast<const uint8*>(signature.data()), signature.size(),
reinterpret_cast<const uint8*>(spki.data()), spki.size());
if (!ok) {
diff --git a/net/quic/crypto/proof_verifier_chromium.h b/net/quic/crypto/proof_verifier_chromium.h
index 8786e52e7d..4969cc8aa5 100644
--- a/net/quic/crypto/proof_verifier_chromium.h
+++ b/net/quic/crypto/proof_verifier_chromium.h
@@ -39,8 +39,7 @@ class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier {
virtual ~ProofVerifierChromium();
// ProofVerifier interface
- virtual Status VerifyProof(QuicVersion version,
- const std::string& hostname,
+ virtual Status VerifyProof(const std::string& hostname,
const std::string& server_config,
const std::vector<std::string>& certs,
const std::string& signature,
@@ -60,8 +59,7 @@ class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier {
int DoVerifyCert(int result);
int DoVerifyCertComplete(int result);
- bool VerifySignature(QuicVersion version,
- const std::string& signed_data,
+ bool VerifySignature(const std::string& signed_data,
const std::string& signature,
const std::string& cert);
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index ec87d779d5..ca6941c7e1 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -314,6 +314,12 @@ void QuicClientSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
NotifyFactoryOfSessionCloseLater();
}
+void QuicClientSession::OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) {
+ logger_.OnSuccessfulVersionNegotiation(version);
+ QuicSession::OnSuccessfulVersionNegotiation(version);
+}
+
void QuicClientSession::StartReading() {
if (read_pending_) {
return;
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index a5973b6b61..d167237d80 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -113,6 +113,8 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession {
// QuicConnectionVisitorInterface methods:
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE;
// Performs a crypto handshake with the server.
int CryptoConnect(bool require_confirmation,
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 748821e445..7417bd435a 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -19,6 +19,7 @@ using std::list;
using std::make_pair;
using std::min;
using std::max;
+using std::numeric_limits;
using std::vector;
using std::set;
using std::string;
@@ -32,7 +33,7 @@ const QuicPacketSequenceNumber kMaxPacketGap = 5000;
// 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;
+const size_t kMaxRetransmissionsPerAck = 10;
// TCP retransmits after 2 nacks. We allow for a third in case of out-of-order
// delivery.
@@ -125,6 +126,21 @@ class TimeoutAlarm : public QuicAlarm::Delegate {
QuicConnection* connection_;
};
+// Indicates if any of the frames are intended to be sent with FORCE.
+// Returns true when one of the frames is a CONNECTION_CLOSE_FRAME.
+net::QuicConnection::Force HasForcedFrames(
+ const RetransmittableFrames* retransmittable_frames) {
+ if (!retransmittable_frames) {
+ return net::QuicConnection::NO_FORCE;
+ }
+ for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
+ if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
+ return net::QuicConnection::FORCE;
+ }
+ }
+ return net::QuicConnection::NO_FORCE;
+}
+
} // namespace
// TODO(rch): Remove this.
@@ -168,6 +184,7 @@ QuicConnection::QuicConnection(QuicGuid guid,
time_of_last_received_packet_(clock_->ApproximateNow()),
time_of_last_sent_packet_(clock_->ApproximateNow()),
congestion_manager_(clock_, kTCP),
+ sent_packet_manager_(is_server, this),
version_negotiation_state_(START_NEGOTIATION),
max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm),
is_server_(is_server),
@@ -192,7 +209,6 @@ QuicConnection::QuicConnection(QuicGuid guid,
QuicConnection::~QuicConnection() {
STLDeleteElements(&ack_notifiers_);
STLDeleteElements(&undecryptable_packets_);
- STLDeleteValues(&unacked_packets_);
STLDeleteValues(&group_map_);
for (QueuedPacketList::iterator it = queued_packets_.begin();
it != queued_packets_.end(); ++it) {
@@ -282,6 +298,7 @@ bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) {
}
version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(received_version);
// Store the new version.
framer_.set_version(received_version);
@@ -380,6 +397,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
DCHECK_EQ(1u, header.public_header.versions.size());
DCHECK_EQ(header.public_header.versions[0], version());
version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(version());
}
} else {
DCHECK(!header.public_header.version_flag);
@@ -387,6 +405,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// it should stop sending version since the version negotiation is done.
packet_creator_.StopSendingVersion();
version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(version());
}
}
@@ -433,6 +452,17 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
SendConnectionClose(QUIC_INVALID_ACK_DATA);
return false;
}
+
+ // Reset the RTO timeout for each packet when an ack is received.
+ if (retransmission_alarm_->IsSet()) {
+ retransmission_alarm_->Cancel();
+ QuicTime::Delta retransmission_delay =
+ congestion_manager_.GetRetransmissionDelay(
+ sent_packet_manager_.GetNumUnackedPackets(), 0);
+ retransmission_alarm_->Set(clock_->ApproximateNow().Add(
+ retransmission_delay));
+ }
+
last_ack_frames_.push_back(incoming_ack);
return connected_;
}
@@ -452,12 +482,11 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
sent_entropy_manager_.ClearEntropyBefore(
received_packet_manager_.least_packet_awaited_by_peer() - 1);
+ retransmitted_nacked_packet_count_ = 0;
SequenceNumberSet acked_packets;
- HandleAckForSentPackets(incoming_ack, &acked_packets);
- HandleAckForSentFecPackets(incoming_ack, &acked_packets);
+ sent_packet_manager_.HandleAckForSentPackets(incoming_ack, &acked_packets);
+ sent_packet_manager_.HandleAckForSentFecPackets(incoming_ack, &acked_packets);
if (acked_packets.size() > 0) {
- visitor_->OnAck(acked_packets);
-
// Inform all the registered AckNotifiers of the new ACKs.
// TODO(rjshade): Make this more efficient by maintaining a mapping of
// <sequence number, set<AckNotifierList>> so that OnAck
@@ -475,6 +504,13 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
}
}
}
+ // Clear the earliest retransmission timeouts that are no longer unacked to
+ // ensure the priority queue doesn't become too large.
+ while (!retransmission_timeouts_.empty() &&
+ !sent_packet_manager_.IsUnacked(
+ retransmission_timeouts_.top().sequence_number)) {
+ retransmission_timeouts_.pop();
+ }
congestion_manager_.OnIncomingAckFrame(incoming_ack,
time_of_last_received_packet_);
}
@@ -563,73 +599,6 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
return true;
}
-void QuicConnection::HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets) {
- int retransmitted_packets = 0;
- // Go through the packets we have not received an ack for and see if this
- // incoming_ack shows they've been seen by the peer.
- UnackedPacketMap::iterator it = unacked_packets_.begin();
- while (it != unacked_packets_.end()) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (sequence_number >
- received_packet_manager_.peer_largest_observed_packet()) {
- // These are very new sequence_numbers.
- break;
- }
- RetransmittableFrames* unacked = it->second;
- if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
- // Packet was acked, so remove it from our unacked packet list.
- DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
- acked_packets->insert(sequence_number);
- delete unacked;
- unacked_packets_.erase(it++);
- retransmission_map_.erase(sequence_number);
- } else {
- // This is a packet which we planned on retransmitting and has not been
- // seen at the time of this ack being sent out. See if it's our new
- // lowest unacked packet.
- DVLOG(1) << ENDPOINT << "still missing packet " << sequence_number;
- ++it;
- // The peer got packets after this sequence number. This is an explicit
- // nack.
- RetransmissionMap::iterator retransmission_it =
- retransmission_map_.find(sequence_number);
- ++(retransmission_it->second.number_nacks);
- if (retransmission_it->second.number_nacks >=
- kNumberOfNacksBeforeRetransmission &&
- retransmitted_packets < kMaxRetransmissionsPerAck) {
- ++retransmitted_packets;
- DVLOG(1) << ENDPOINT << "Trying to retransmit packet "
- << sequence_number
- << " as it has been nacked 3 or more times.";
- // RetransmitPacket will retransmit with a new sequence_number.
- RetransmitPacket(sequence_number);
- }
- }
- }
-}
-
-void QuicConnection::HandleAckForSentFecPackets(
- const QuicAckFrame& incoming_ack, SequenceNumberSet* acked_packets) {
- UnackedPacketMap::iterator it = unacked_fec_packets_.begin();
- while (it != unacked_fec_packets_.end()) {
- QuicPacketSequenceNumber sequence_number = it->first;
- if (sequence_number >
- received_packet_manager_.peer_largest_observed_packet()) {
- break;
- }
- if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
- DVLOG(1) << ENDPOINT << "Got an ack for fec packet: " << sequence_number;
- acked_packets->insert(sequence_number);
- unacked_fec_packets_.erase(it++);
- } else {
- DVLOG(1) << ENDPOINT << "Still missing ack for fec packet: "
- << sequence_number;
- ++it;
- }
- }
-}
-
void QuicConnection::OnFecData(const QuicFecData& fec) {
DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group);
DCHECK_NE(0u, last_header_.fec_group);
@@ -699,9 +668,8 @@ void QuicConnection::OnPacketComplete() {
// from unacket_packets_, increasing the least_unacked.
const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck();
- if ((last_stream_frames_.empty() ||
- visitor_->OnPacket(self_address_, peer_address_,
- last_header_, last_stream_frames_))) {
+ if (last_stream_frames_.empty() ||
+ visitor_->OnStreamFrames(last_stream_frames_)) {
received_packet_manager_.RecordPacketReceived(
last_header_, time_of_last_received_packet_);
}
@@ -759,13 +727,10 @@ bool QuicConnection::ShouldLastPacketInstigateAck() {
// the high water mark.
if (!last_ack_frames_.empty() &&
!last_ack_frames_.back().received_info.missing_packets.empty() &&
- !unacked_packets_.empty()) {
- if (unacked_packets_.begin()->first >
- *last_ack_frames_.back().received_info.missing_packets.begin()) {
- return true;
- }
+ sent_packet_manager_.HasUnackedPackets()) {
+ return sent_packet_manager_.GetLeastUnackedSentPacket() >
+ *last_ack_frames_.back().received_info.missing_packets.begin();
}
-
return false;
}
@@ -781,7 +746,7 @@ void QuicConnection::MaybeSendInResponseToPacket(
// Set the ack alarm for when any retransmittable frame is received.
if (!ack_alarm_->IsSet()) {
ack_alarm_->Set(clock_->ApproximateNow().Add(
- congestion_manager_.DefaultRetransmissionTime()));
+ congestion_manager_.DelayedAckTime()));
}
}
send_ack_in_response_to_packet_ = !send_ack_in_response_to_packet_;
@@ -817,39 +782,85 @@ void QuicConnection::SendVersionNegotiationPacket() {
delete encrypted;
}
-QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
- bool fin) {
- // To make reasoning about crypto frames easier, we don't combine them with
- // any other frames in a single packet.
- const bool crypto_frame_while_batch_mode =
- id == kCryptoStreamId && packet_generator_.InBatchMode();
+QuicConsumedData QuicConnection::SendvStreamDataInner(
+ QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier* notifier) {
+ // TODO(ianswett): Further improve sending by passing the iovec down
+ // instead of batching into multiple stream frames in a single packet.
+ const bool already_in_batch_mode = packet_generator_.InBatchMode();
+ packet_generator_.StartBatchOperations();
- if (crypto_frame_while_batch_mode) {
- // Flush pending frames to make room for a crypto frame.
- packet_generator_.FinishBatchOperations();
+ size_t bytes_written = 0;
+ bool fin_consumed = false;
+
+ for (int i = 0; i < iov_count; ++i) {
+ bool send_fin = fin && (i == iov_count - 1);
+ if (!send_fin && iov[i].iov_len == 0) {
+ LOG(DFATAL) << "Attempt to send empty stream frame";
+ }
+
+ StringPiece data(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ int currentOffset = offset + bytes_written;
+ QuicConsumedData consumed_data =
+ packet_generator_.ConsumeData(id,
+ data,
+ currentOffset,
+ send_fin,
+ notifier);
+
+ DCHECK_LE(consumed_data.bytes_consumed, numeric_limits<uint32>::max());
+ bytes_written += consumed_data.bytes_consumed;
+ fin_consumed = consumed_data.fin_consumed;
+ // If no bytes were consumed, bail now, because the stream can not write
+ // more data.
+ if (consumed_data.bytes_consumed < iov[i].iov_len) {
+ break;
+ }
}
- QuicConsumedData consumed_data =
- packet_generator_.ConsumeData(id, data, offset, fin);
- if (crypto_frame_while_batch_mode) {
- // Restore batch mode.
- packet_generator_.StartBatchOperations();
+ // Handle the 0 byte write properly.
+ if (iov_count == 0) {
+ DCHECK(fin);
+ QuicConsumedData consumed_data = packet_generator_.ConsumeData(
+ id, StringPiece(), offset, fin, NULL);
+ fin_consumed = consumed_data.fin_consumed;
}
- return consumed_data;
+
+ // Leave the generator in the original batch state.
+ if (!already_in_batch_mode) {
+ packet_generator_.FinishBatchOperations();
+ }
+ DCHECK_EQ(already_in_batch_mode, packet_generator_.InBatchMode());
+
+ return QuicConsumedData(bytes_written, fin_consumed);
}
-QuicConsumedData QuicConnection::SendStreamDataAndNotifyWhenAcked(
+QuicConsumedData QuicConnection::SendvStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin) {
+ return SendvStreamDataInner(id, iov, iov_count, offset, fin, NULL);
+}
+
+QuicConsumedData QuicConnection::SendvStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- StringPiece data,
+ const struct iovec* iov,
+ int iov_count,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate) {
+ if (!fin && iov_count == 0) {
+ LOG(DFATAL) << "Attempt to send empty stream frame";
+ }
// This notifier will be deleted in ProcessAckFrame once it has seen ACKs for
// all the consumed data (or below if no data was consumed).
QuicAckNotifier* notifier = new QuicAckNotifier(delegate);
QuicConsumedData consumed_data =
- packet_generator_.ConsumeData(id, data, offset, fin, notifier);
+ SendvStreamDataInner(id, iov, iov_count, offset, fin, notifier);
if (consumed_data.bytes_consumed > 0) {
// If some data was consumed, then the delegate should be registered for
@@ -938,38 +949,30 @@ bool QuicConnection::DoWrite() {
DCHECK(!write_blocked_);
WriteQueuedPackets();
- // We are postulating if we are not yet forward secure, the visitor may have
- // handshake messages to send.
- // TODO(jar): add a new visitor_ method that returns whether it has handshake
- // messages to send, and call it and pass the return value to each CanWrite
- // call.
- const IsHandshake maybe_handshake =
- encryption_level_ == ENCRYPTION_FORWARD_SECURE ? NOT_HANDSHAKE
- : IS_HANDSHAKE;
-
+ IsHandshake pending_handshake = visitor_->HasPendingHandshake() ?
+ IS_HANDSHAKE : NOT_HANDSHAKE;
// Sending queued packets may have caused the socket to become write blocked,
// or the congestion manager to prohibit sending. If we've sent everything
// we had queued and we're still not blocked, let the visitor know it can
// write more.
if (CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- maybe_handshake)) {
- const bool in_batch_mode = packet_generator_.InBatchMode();
- if (!in_batch_mode) {
+ pending_handshake)) {
+ const bool already_in_batch_mode = packet_generator_.InBatchMode();
+ if (!already_in_batch_mode) {
packet_generator_.StartBatchOperations();
}
bool all_bytes_written = visitor_->OnCanWrite();
- if (!in_batch_mode) {
+ if (!already_in_batch_mode) {
packet_generator_.FinishBatchOperations();
}
// After the visitor writes, it may have caused the socket to become write
// blocked or the congestion manager to prohibit sending, so check again.
- // TODO(jar): we need to pass NOT_HANDSHAKE instead of maybe_handshake to
- // this CanWrite call to avoid getting into an infinite loop calling
- // DoWrite.
+ pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE
+ : NOT_HANDSHAKE;
if (!write_blocked_ && !all_bytes_written &&
CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
- NOT_HANDSHAKE)) {
+ pending_handshake)) {
// We're not write blocked, but some stream didn't write out all of its
// bytes. Register for 'immediate' resumption so we'll keep writing after
// other quic connections have had a chance to use the socket.
@@ -1003,7 +1006,7 @@ bool QuicConnection::WriteQueuedPackets() {
packet_iterator->sequence_number,
packet_iterator->packet,
packet_iterator->retransmittable,
- NO_FORCE)) {
+ packet_iterator->forced)) {
packet_iterator = queued_packets_.erase(packet_iterator);
} else {
// Continue, because some queued packets may still be writable.
@@ -1017,101 +1020,72 @@ bool QuicConnection::WriteQueuedPackets() {
bool QuicConnection::MaybeRetransmitPacketForRTO(
QuicPacketSequenceNumber sequence_number) {
- DCHECK_EQ(ContainsKey(unacked_packets_, sequence_number),
- ContainsKey(retransmission_map_, sequence_number));
-
- if (!ContainsKey(unacked_packets_, sequence_number)) {
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
DVLOG(2) << ENDPOINT << "alarm fired for " << sequence_number
<< " but it has been acked or already retransmitted with"
- << " different sequence number.";
+ << " a different sequence number.";
// So no extra delay is added for this packet.
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 >
- received_packet_manager_.peer_largest_observed_packet() &&
+ if (received_truncated_ack_ &&
+ sequence_number > GetPeerLargestObservedPacket() &&
// 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) {
+ !sent_packet_manager_.IsRetransmission(sequence_number)) {
return false;
- } else {
- ++stats_.rto_count;
- RetransmitPacket(sequence_number);
- return true;
}
+
+ ++stats_.rto_count;
+ RetransmitPacket(sequence_number);
+ return true;
}
void QuicConnection::RetransmitUnackedPackets(
RetransmissionType retransmission_type) {
- if (unacked_packets_.empty()) {
+ SequenceNumberSet unacked_packets = sent_packet_manager_.GetUnackedPackets();
+ if (unacked_packets.empty()) {
return;
}
- UnackedPacketMap::iterator next_it = unacked_packets_.begin();
- QuicPacketSequenceNumber end_sequence_number =
- unacked_packets_.rbegin()->first;
- do {
- UnackedPacketMap::iterator current_it = next_it;
- ++next_it;
+ for (SequenceNumberSet::const_iterator unacked_it = unacked_packets.begin();
+ unacked_it != unacked_packets.end(); ++unacked_it) {
+ const RetransmittableFrames& frames =
+ sent_packet_manager_.GetRetransmittableFrames(*unacked_it);
if (retransmission_type == ALL_PACKETS ||
- current_it->second->encryption_level() == ENCRYPTION_INITIAL) {
+ frames.encryption_level() == ENCRYPTION_INITIAL) {
// TODO(satyamshekhar): Think about congestion control here.
// Specifically, about the retransmission count of packets being sent
// proactively to achieve 0 (minimal) RTT.
- RetransmitPacket(current_it->first);
+ RetransmitPacket(*unacked_it);
}
- } while (next_it != unacked_packets_.end() &&
- next_it->first <= end_sequence_number);
+ }
}
void QuicConnection::RetransmitPacket(
QuicPacketSequenceNumber sequence_number) {
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(sequence_number);
- RetransmissionMap::iterator retransmission_it =
- retransmission_map_.find(sequence_number);
- // There should always be an entry corresponding to |sequence_number| in
- // both |retransmission_map_| and |unacked_packets_|. Retransmissions due to
- // RTO for sequence numbers that are already acked or retransmitted are
- // ignored by MaybeRetransmitPacketForRTO.
- DCHECK(unacked_it != unacked_packets_.end());
- DCHECK(retransmission_it != retransmission_map_.end());
- RetransmittableFrames* unacked = unacked_it->second;
+ DCHECK(sent_packet_manager_.IsUnacked(sequence_number));
+
// TODO(pwestin): Need to fix potential issue with FEC and a 1 packet
// congestion window see b/8331807 for details.
congestion_manager_.AbandoningPacket(sequence_number);
+ const RetransmittableFrames& retransmittable_frames =
+ sent_packet_manager_.GetRetransmittableFrames(sequence_number);
+
// Re-packetize the frames with a new sequence number for retransmission.
// Retransmitted data packets do not use FEC, even when it's enabled.
// Retransmitted packets use the same sequence number length as the original.
QuicSequenceNumberLength original_sequence_number_length =
- retransmission_it->second.sequence_number_length;
+ sent_packet_manager_.GetSequenceNumberLength(sequence_number);
SerializedPacket serialized_packet =
- packet_creator_.ReserializeAllFrames(unacked->frames(),
+ packet_creator_.ReserializeAllFrames(retransmittable_frames.frames(),
original_sequence_number_length);
- RetransmissionInfo retransmission_info(
- serialized_packet.sequence_number,
- serialized_packet.sequence_number_length);
- retransmission_info.number_retransmissions =
- retransmission_it->second.number_retransmissions + 1;
- // Remove info with old sequence number.
- unacked_packets_.erase(unacked_it);
- retransmission_map_.erase(retransmission_it);
- DLOG(INFO) << ENDPOINT << "Retransmitting unacked packet " << sequence_number
- << " as " << serialized_packet.sequence_number;
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first < serialized_packet.sequence_number);
- unacked_packets_.insert(make_pair(serialized_packet.sequence_number,
- unacked));
- retransmission_map_.insert(make_pair(serialized_packet.sequence_number,
- retransmission_info));
// A notifier may be waiting to hear about ACKs for the original sequence
// number. Inform them that the sequence number has changed.
@@ -1121,15 +1095,21 @@ void QuicConnection::RetransmitPacket(
serialized_packet.sequence_number);
}
+ DLOG(INFO) << ENDPOINT << "Retransmitting " << sequence_number << " as "
+ << serialized_packet.sequence_number;
if (debug_visitor_) {
debug_visitor_->OnPacketRetransmitted(sequence_number,
serialized_packet.sequence_number);
}
- SendOrQueuePacket(unacked->encryption_level(),
+ sent_packet_manager_.OnRetransmittedPacket(sequence_number,
+ serialized_packet.sequence_number);
+
+ SendOrQueuePacket(retransmittable_frames.encryption_level(),
serialized_packet.sequence_number,
serialized_packet.packet,
serialized_packet.entropy_hash,
- HAS_RETRANSMITTABLE_DATA);
+ HAS_RETRANSMITTABLE_DATA,
+ HasForcedFrames(serialized_packet.retransmittable_frames));
}
bool QuicConnection::CanWrite(Retransmission retransmission,
@@ -1157,30 +1137,22 @@ bool QuicConnection::CanWrite(Retransmission retransmission,
return true;
}
-bool QuicConnection::IsRetransmission(
- QuicPacketSequenceNumber sequence_number) {
- RetransmissionMap::iterator it = retransmission_map_.find(sequence_number);
- return it != retransmission_map_.end() &&
- it->second.number_retransmissions > 0;
-}
-
void QuicConnection::SetupRetransmission(
QuicPacketSequenceNumber sequence_number,
EncryptionLevel level) {
- RetransmissionMap::iterator it = retransmission_map_.find(sequence_number);
- if (it == retransmission_map_.end()) {
+ if (!sent_packet_manager_.IsUnacked(sequence_number)) {
DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number;
return;
}
-
- RetransmissionInfo retransmission_info = it->second;
+ size_t retransmission_count =
+ sent_packet_manager_.GetRetransmissionCount(sequence_number);
// TODO(rch): consider using a much smaller retransmisison_delay
// for the ENCRYPTION_NONE packets.
size_t effective_retransmission_count =
- level == ENCRYPTION_NONE ? 0 : retransmission_info.number_retransmissions;
+ level == ENCRYPTION_NONE ? 0 : retransmission_count;
QuicTime::Delta retransmission_delay =
congestion_manager_.GetRetransmissionDelay(
- unacked_packets_.size(),
+ sent_packet_manager_.GetNumUnackedPackets(),
effective_retransmission_count);
retransmission_timeouts_.push(RetransmissionTime(
@@ -1201,7 +1173,6 @@ void QuicConnection::SetupRetransmission(
void QuicConnection::SetupAbandonFecTimer(
QuicPacketSequenceNumber sequence_number) {
- DCHECK(ContainsKey(unacked_fec_packets_, sequence_number));
QuicTime::Delta retransmission_delay =
QuicTime::Delta::FromMilliseconds(
congestion_manager_.DefaultRetransmissionTime().ToMilliseconds() * 3);
@@ -1211,21 +1182,6 @@ void QuicConnection::SetupAbandonFecTimer(
true));
}
-void QuicConnection::DropPacket(QuicPacketSequenceNumber sequence_number) {
- UnackedPacketMap::iterator unacked_it =
- unacked_packets_.find(sequence_number);
- // Packet was not meant to be retransmitted.
- if (unacked_it == unacked_packets_.end()) {
- DCHECK(!ContainsKey(retransmission_map_, sequence_number));
- return;
- }
- // Delete the unacked packet.
- delete unacked_it->second;
- unacked_packets_.erase(unacked_it);
- retransmission_map_.erase(sequence_number);
- return;
-}
-
bool QuicConnection::WritePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
@@ -1244,14 +1200,15 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
level == ENCRYPTION_NONE) {
// Drop packets that are NULL encrypted since the peer won't accept them
// anymore.
- DLOG(INFO) << ENDPOINT << "Dropped packet: " << sequence_number
+ DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number
<< " since the packet is NULL encrypted.";
- DropPacket(sequence_number);
+ sent_packet_manager_.DiscardPacket(sequence_number);
delete packet;
return true;
}
- Retransmission retransmission = IsRetransmission(sequence_number) ?
+ Retransmission retransmission =
+ sent_packet_manager_.IsRetransmission(sequence_number) ?
IS_RETRANSMISSION : NOT_RETRANSMISSION;
// TODO(wtc): use the same logic that is used in the packet generator.
// Namely, a packet is a handshake if it contains a stream frame for the
@@ -1305,7 +1262,11 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
// 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_->IsWriteBlockedDataBuffered()) {
+ delete packet;
+ return true;
+ }
+ return false;
}
// We can't send an error as the socket is presumably borked.
CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
@@ -1323,11 +1284,13 @@ bool QuicConnection::WritePacket(EncryptionLevel level,
// TODO(ianswett): Change the sequence number length and other packet creator
// options by a more explicit API than setting a struct value directly.
- packet_creator_.options()->send_sequence_number_length =
- CalculateSequenceNumberLength(sequence_number);
+ packet_creator_.UpdateSequenceNumberLength(
+ received_packet_manager_.least_packet_awaited_by_peer(),
+ congestion_manager_.BandwidthEstimate().ToBytesPerPeriod(
+ congestion_manager_.SmoothedRtt()));
congestion_manager_.SentPacket(sequence_number, now, packet->length(),
- retransmission);
+ retransmission, retransmittable);
stats_.bytes_sent += encrypted->length();
++stats_.packets_sent;
@@ -1355,73 +1318,51 @@ int QuicConnection::WritePacketToWire(QuicPacketSequenceNumber sequence_number,
return bytes_written;
}
-QuicSequenceNumberLength QuicConnection::CalculateSequenceNumberLength(
- QuicPacketSequenceNumber sequence_number) {
- DCHECK_LE(received_packet_manager_.least_packet_awaited_by_peer(),
- sequence_number);
- // Since the packet creator will not change sequence number length mid FEC
- // group, include the size of an FEC group to be safe.
- const QuicPacketSequenceNumber current_delta =
- packet_creator_.options()->max_packets_per_fec_group + sequence_number
- - received_packet_manager_.least_packet_awaited_by_peer();
- const uint64 congestion_window =
- congestion_manager_.BandwidthEstimate().ToBytesPerPeriod(
- congestion_manager_.SmoothedRtt()) /
- packet_creator_.options()->max_packet_length;
- const uint64 delta = max(current_delta, congestion_window);
-
- if (delta < 1 << ((PACKET_1BYTE_SEQUENCE_NUMBER * 8) - 2)) {
- return PACKET_1BYTE_SEQUENCE_NUMBER;
- } else if (delta < 1 << ((PACKET_2BYTE_SEQUENCE_NUMBER * 8) - 2)) {
- return PACKET_2BYTE_SEQUENCE_NUMBER;
- } else if (delta < 1 << ((PACKET_4BYTE_SEQUENCE_NUMBER * 8) - 2)) {
- return PACKET_4BYTE_SEQUENCE_NUMBER;
- }
- return PACKET_6BYTE_SEQUENCE_NUMBER;
-}
-
bool QuicConnection::OnSerializedPacket(
const SerializedPacket& serialized_packet) {
- if (serialized_packet.retransmittable_frames != NULL) {
- DCHECK(unacked_packets_.empty() ||
- unacked_packets_.rbegin()->first <
- serialized_packet.sequence_number);
- // Retransmitted frames will be sent with the same encryption level as the
- // original.
+ if (serialized_packet.retransmittable_frames) {
serialized_packet.retransmittable_frames->set_encryption_level(
encryption_level_);
- unacked_packets_.insert(
- make_pair(serialized_packet.sequence_number,
- serialized_packet.retransmittable_frames));
- // All unacked packets might be retransmitted.
- retransmission_map_.insert(
- make_pair(serialized_packet.sequence_number,
- RetransmissionInfo(
- serialized_packet.sequence_number,
- serialized_packet.sequence_number_length)));
- } else if (serialized_packet.packet->is_fec_packet()) {
- unacked_fec_packets_.insert(make_pair(
- serialized_packet.sequence_number,
- serialized_packet.retransmittable_frames));
}
+ sent_packet_manager_.OnSerializedPacket(serialized_packet);
return SendOrQueuePacket(encryption_level_,
serialized_packet.sequence_number,
serialized_packet.packet,
serialized_packet.entropy_hash,
serialized_packet.retransmittable_frames != NULL ?
HAS_RETRANSMITTABLE_DATA :
- NO_RETRANSMITTABLE_DATA);
+ NO_RETRANSMITTABLE_DATA,
+ HasForcedFrames(
+ serialized_packet.retransmittable_frames));
+}
+
+QuicPacketSequenceNumber QuicConnection::GetPeerLargestObservedPacket() {
+ return received_packet_manager_.peer_largest_observed_packet();
+}
+
+QuicPacketSequenceNumber QuicConnection::GetNextPacketSequenceNumber() {
+ return packet_creator_.sequence_number() + 1;
+}
+
+void QuicConnection::OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) {
+ if (nack_count >= kNumberOfNacksBeforeRetransmission &&
+ retransmitted_nacked_packet_count_ < kMaxRetransmissionsPerAck) {
+ ++retransmitted_nacked_packet_count_;
+ RetransmitPacket(sequence_number);
+ }
}
bool QuicConnection::SendOrQueuePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable) {
+ HasRetransmittableData retransmittable,
+ Force forced) {
sent_entropy_manager_.RecordPacketEntropyHash(sequence_number, entropy_hash);
- if (!WritePacket(level, sequence_number, packet, retransmittable, NO_FORCE)) {
+ if (!WritePacket(level, sequence_number, packet, retransmittable, forced)) {
queued_packets_.push_back(QueuedPacket(sequence_number, packet, level,
- retransmittable));
+ retransmittable, forced));
return false;
}
return true;
@@ -1437,14 +1378,7 @@ bool QuicConnection::ShouldSimulateLostPacket() {
}
void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) {
- if (!unacked_packets_.empty()) {
- sent_info->least_unacked = unacked_packets_.begin()->first;
- } else {
- // If there are no unacked packets, set the least unacked packet to
- // sequence_number() + 1 since that will be the sequence number of this
- // ack packet whenever it is sent.
- sent_info->least_unacked = packet_creator_.sequence_number() + 1;
- }
+ sent_info->least_unacked = sent_packet_manager_.GetLeastUnackedSentPacket();
sent_info->entropy_hash = sent_entropy_manager_.EntropyHash(
sent_info->least_unacked - 1);
}
@@ -1468,13 +1402,12 @@ void QuicConnection::SendAck() {
void QuicConnection::MaybeAbandonFecPacket(
QuicPacketSequenceNumber sequence_number) {
- if (!ContainsKey(unacked_fec_packets_, sequence_number)) {
+ if (!sent_packet_manager_.IsFecUnacked(sequence_number)) {
DVLOG(2) << ENDPOINT << "no need to abandon fec packet: "
<< sequence_number << "; it's already acked'";
return;
}
congestion_manager_.AbandoningPacket(sequence_number);
- // TODO(satyashekhar): Should this decrease the congestion window?
}
QuicTime QuicConnection::OnRetransmissionTimeout() {
@@ -1641,36 +1574,6 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) {
SendConnectionCloseWithDetails(error, string());
}
-void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
- const string& details) {
- DLOG(INFO) << ENDPOINT << "Force closing with error "
- << QuicUtils::ErrorToString(error) << " (" << error << ") "
- << details;
- QuicConnectionCloseFrame frame;
- frame.error_code = error;
- frame.error_details = details;
- UpdateSentPacketInfo(&frame.ack_frame.sent_info);
- received_packet_manager_.UpdateReceivedPacketInfo(
- &frame.ack_frame.received_info, clock_->ApproximateNow());
-
- SerializedPacket serialized_packet =
- packet_creator_.SerializeConnectionClose(&frame);
-
- // We need to update the sent entropy hash for all sent packets.
- sent_entropy_manager_.RecordPacketEntropyHash(
- serialized_packet.sequence_number,
- serialized_packet.entropy_hash);
-
- if (!WritePacket(encryption_level_,
- serialized_packet.sequence_number,
- serialized_packet.packet,
- serialized_packet.retransmittable_frames != NULL ?
- HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA,
- FORCE)) {
- delete serialized_packet.packet;
- }
-}
-
void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
const string& details) {
if (!write_blocked_) {
@@ -1679,6 +1582,21 @@ void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
CloseConnection(error, false);
}
+void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
+ const string& details) {
+ DLOG(INFO) << ENDPOINT << "Force closing with error "
+ << QuicUtils::ErrorToString(error) << " (" << error << ") "
+ << details;
+ QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
+ frame->error_code = error;
+ frame->error_details = details;
+ UpdateSentPacketInfo(&frame->ack_frame.sent_info);
+ received_packet_manager_.UpdateReceivedPacketInfo(
+ &frame->ack_frame.received_info, clock_->ApproximateNow());
+ packet_generator_.AddControlFrame(QuicFrame(frame));
+ Flush();
+}
+
void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
DCHECK(connected_);
connected_ = false;
@@ -1716,6 +1634,14 @@ void QuicConnection::CloseFecGroupsBefore(
}
}
+void QuicConnection::Flush() {
+ if (!packet_generator_.InBatchMode()) {
+ return;
+ }
+ packet_generator_.FinishBatchOperations();
+ packet_generator_.StartBatchOperations();
+}
+
bool QuicConnection::HasQueuedData() const {
return !queued_packets_.empty() || packet_generator_.HasQueuedFrames();
}
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index a23ecf049c..1524909772 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -24,6 +24,7 @@
#include <vector>
#include "base/containers/hash_tables.h"
+#include "net/base/iovec.h"
#include "net/base/ip_endpoint.h"
#include "net/base/linked_hash_map.h"
#include "net/quic/congestion_control/quic_congestion_manager.h"
@@ -37,6 +38,7 @@
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_received_packet_manager.h"
#include "net/quic/quic_sent_entropy_manager.h"
+#include "net/quic/quic_sent_packet_manager.h"
namespace net {
@@ -49,6 +51,8 @@ namespace test {
class QuicConnectionPeer;
} // namespace test
+// Class that receives callbacks from the connection when frames are received
+// and when other interesting events happen.
class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
public:
virtual ~QuicConnectionVisitorInterface() {}
@@ -57,10 +61,7 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
// should determine if all frames will be accepted, and return true if so.
// If any frames can't be processed or buffered, none of the data should
// be used, and the callee should return false.
- virtual bool OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame) = 0;
+ virtual bool OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
// Called when the stream is reset by the peer.
virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
@@ -73,13 +74,16 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
virtual void ConnectionClose(QuicErrorCode error,
bool from_peer) = 0;
- // Called when packets are acked by the peer.
- virtual void OnAck(const SequenceNumberSet& acked_packets) = 0;
+ // Called once a specific QUIC version is agreed by both endpoints.
+ virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
// Called when a blocked socket becomes writable. If all pending bytes for
// this visitor are consumed by the connection successfully this should
// return true, otherwise it should return false.
virtual bool OnCanWrite() = 0;
+
+ // Called to ask if any handshake messages are pending in this visitor.
+ virtual bool HasPendingHandshake() const = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
@@ -184,7 +188,8 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
class NET_EXPORT_PRIVATE QuicConnection
: public QuicFramerVisitorInterface,
public QuicBlockedWriterInterface,
- public QuicPacketGenerator::DelegateInterface {
+ public QuicPacketGenerator::DelegateInterface,
+ public QuicSentPacketManager::HelperInterface {
public:
enum Force {
NO_FORCE,
@@ -205,21 +210,24 @@ class NET_EXPORT_PRIVATE QuicConnection
QuicVersion version);
virtual ~QuicConnection();
- // Send the data payload to the peer.
+ // Send the data in |iov| to the peer in as few packets as possible.
// Returns a pair with the number of bytes consumed from data, and a boolean
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
// if the socket was unexpectedly blocked.
- QuicConsumedData SendStreamData(QuicStreamId id,
- base::StringPiece data,
- QuicStreamOffset offset,
- bool fin);
- // Same as above, except that the provided delegate will be informed once ACKs
- // have been received for all the packets written.
+ QuicConsumedData SendvStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin);
+
+ // Same as SendvStreamData, except the provided delegate will be informed
+ // once ACKs have been received for all the packets written.
// The |delegate| is not owned by the QuicConnection and must outlive it.
- QuicConsumedData SendStreamDataAndNotifyWhenAcked(
+ QuicConsumedData SendvStreamDataAndNotifyWhenAcked(
QuicStreamId id,
- base::StringPiece data,
+ const struct iovec* iov,
+ int iov_count,
QuicStreamOffset offset,
bool fin,
QuicAckNotifier::DelegateInterface* delegate);
@@ -240,7 +248,7 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual void SendConnectionCloseWithDetails(QuicErrorCode error,
const std::string& details);
// Notifies the visitor of the close and marks the connection as disconnected.
- void CloseConnection(QuicErrorCode error, bool from_peer);
+ virtual void CloseConnection(QuicErrorCode error, bool from_peer) OVERRIDE;
virtual void SendGoAway(QuicErrorCode error,
QuicStreamId last_good_stream_id,
const std::string& reason);
@@ -303,6 +311,12 @@ class NET_EXPORT_PRIVATE QuicConnection
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
+ // QuicSentPacketManager::HelperInterface
+ virtual QuicPacketSequenceNumber GetPeerLargestObservedPacket() OVERRIDE;
+ virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() OVERRIDE;
+ virtual void OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) OVERRIDE;
+
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
@@ -335,6 +349,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// Testing only.
size_t NumQueuedPackets() const { return queued_packets_.size(); }
+ // Flush any queued frames immediately. Preserves the batch write mode and
+ // does nothing if there are no pending frames.
+ void Flush();
+
// Returns true if the connection has queued packets or frames.
bool HasQueuedData() const;
@@ -405,14 +423,17 @@ class NET_EXPORT_PRIVATE QuicConnection
// is present in the |retransmission_map_|, then contents of this packet will
// be retransmitted with a new sequence number if it's not acked by the peer.
// Deletes |packet| via WritePacket call or transfers ownership to
- // QueuedPacket, ultimately deleted via WritePacket. Also, it updates the
+ // QueuedPacket, ultimately deleted via WritePacket. Updates the
// entropy map corresponding to |sequence_number| using |entropy_hash|.
+ // |retransmittable| is supplied to the congestion manager, and when |forced|
+ // is true, it bypasses the congestion manager.
// TODO(wtc): none of the callers check the return value.
virtual bool SendOrQueuePacket(EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable);
+ HasRetransmittableData retransmittable,
+ Force forced);
// Writes the given packet to socket, encrypted with |level|, with the help
// of helper. Returns true on successful write, false otherwise. However,
@@ -448,23 +469,35 @@ class NET_EXPORT_PRIVATE QuicConnection
private:
friend class test::QuicConnectionPeer;
+ // Inner helper function to SendvStreamData and
+ // SendvStreamDataAndNotifyWhenAcked.
+ QuicConsumedData SendvStreamDataInner(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier *notifier);
+
// Packets which have not been written to the wire.
// Owns the QuicPacket* packet.
struct QueuedPacket {
QueuedPacket(QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
EncryptionLevel level,
- HasRetransmittableData retransmittable)
+ HasRetransmittableData retransmittable,
+ Force forced)
: sequence_number(sequence_number),
packet(packet),
encryption_level(level),
- retransmittable(retransmittable) {
+ retransmittable(retransmittable),
+ forced(forced) {
}
QuicPacketSequenceNumber sequence_number;
QuicPacket* packet;
const EncryptionLevel encryption_level;
HasRetransmittableData retransmittable;
+ Force forced;
};
struct RetransmissionInfo {
@@ -506,11 +539,7 @@ class NET_EXPORT_PRIVATE QuicConnection
};
typedef std::list<QueuedPacket> QueuedPacketList;
- typedef linked_hash_map<QuicPacketSequenceNumber,
- RetransmittableFrames*> UnackedPacketMap;
typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
- typedef base::hash_map<QuicPacketSequenceNumber,
- RetransmissionInfo> RetransmissionMap;
typedef std::priority_queue<RetransmissionTime,
std::vector<RetransmissionTime>,
RetransmissionTimeComparator>
@@ -562,11 +591,6 @@ class NET_EXPORT_PRIVATE QuicConnection
void ProcessAckFrame(const QuicAckFrame& incoming_ack);
- void HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets);
- void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack,
- SequenceNumberSet* acked_packets);
-
// Update the |sent_info| for an outgoing ack.
void UpdateSentPacketInfo(SentPacketInfo* sent_info);
@@ -605,6 +629,9 @@ class NET_EXPORT_PRIVATE QuicConnection
std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_;
std::vector<QuicRstStreamFrame> last_rst_frames_;
std::vector<QuicGoAwayFrame> last_goaway_frames_;
+ // Then number of packets retransmitted because of nacks
+ // while processed the current ack frame.
+ size_t retransmitted_nacked_packet_count_;
QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
@@ -612,16 +639,6 @@ class NET_EXPORT_PRIVATE QuicConnection
// Largest sequence sent by the peer which had an ack frame (latest ack info).
QuicPacketSequenceNumber largest_seen_packet_with_ack_;
- // When new packets are created which may be retransmitted, they are added
- // to this map, which contains owning pointers to the contained frames.
- UnackedPacketMap unacked_packets_;
-
- // Pending fec packets that have not been acked yet. These packets need to be
- // cleared out of the cgst_window after a timeout since FEC packets are never
- // retransmitted.
- // Ask: What should be the timeout for these packets?
- UnackedPacketMap unacked_fec_packets_;
-
// Collection of packets which were received before encryption was
// established, but which could not be decrypted. We buffer these on
// the assumption that they could not be processed because they were
@@ -636,9 +653,6 @@ class NET_EXPORT_PRIVATE QuicConnection
// contains all packets that have been retransmitted x times.
RetransmissionTimeouts retransmission_timeouts_;
- // Map from sequence number to the retransmission info.
- RetransmissionMap retransmission_map_;
-
// True while OnRetransmissionTimeout is running to prevent
// SetRetransmissionAlarm from being called erroneously.
bool handling_retransmission_timeout_;
@@ -692,6 +706,10 @@ class NET_EXPORT_PRIVATE QuicConnection
// as well as collecting and generating congestion feedback.
QuicCongestionManager congestion_manager_;
+ // Sent packet manager which tracks the status of packets sent by this
+ // connection.
+ QuicSentPacketManager sent_packet_manager_;
+
// The state of connection in version negotiation finite state machine.
QuicVersionNegotiationState version_negotiation_state_;
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
index 9f1bcac501..aaa692b548 100644
--- a/net/quic/quic_connection_helper_test.cc
+++ b/net/quic/quic_connection_helper_test.cc
@@ -9,6 +9,7 @@
#include "net/base/net_errors.h"
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_connection.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
@@ -18,6 +19,8 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::AnyNumber;
+using testing::Return;
namespace net {
namespace test {
@@ -120,11 +123,14 @@ class QuicConnectionHelperTest : public ::testing::Test {
&random_generator_, socket_.get());
send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>();
EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
- WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+ WillRepeatedly(Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
- testing::Return(QuicBandwidth::FromKBitsPerSecond(100)));
+ Return(QuicBandwidth::FromKBitsPerSecond(100)));
EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(
- testing::Return(QuicTime::Delta::FromMilliseconds(100)));
+ Return(QuicTime::Delta::FromMilliseconds(100)));
+ ON_CALL(*send_algorithm_, SentPacket(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_));
connection_->set_visitor(&visitor_);
connection_->SetSendAlgorithm(send_algorithm_);
@@ -309,18 +315,20 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) {
Initialize();
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
QuicTime start = clock_.ApproximateNow();
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _));
// Send a packet.
- connection_->SendStreamData(1, kData, 0, false);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION));
+ struct iovec iov = {const_cast<char*>(kData),
+ static_cast<size_t>(strlen(kData))};
+ connection_->SendvStreamData(1, &iov, 1, 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION, _));
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
runner_->RunNextTask();
@@ -337,17 +345,20 @@ TEST_F(QuicConnectionHelperTest, TestMultipleRetransmission) {
Initialize();
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
QuicTime::Delta kDefaultRetransmissionTime =
QuicTime::Delta::FromMilliseconds(500);
QuicTime start = clock_.ApproximateNow();
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _));
+
// Send a packet.
- connection_->SendStreamData(1, kData, 0, false);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION));
+ struct iovec iov = {const_cast<char*>(kData),
+ static_cast<size_t>(strlen(kData))};
+ connection_->SendvStreamData(1, &iov, 1, 0, false);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION, _));
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
runner_->RunNextTask();
@@ -357,7 +368,7 @@ TEST_F(QuicConnectionHelperTest, TestMultipleRetransmission) {
// Since no ack was received, the retransmission alarm will fire and
// retransmit it.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 3, _, IS_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 3, _, IS_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(2, _));
runner_->RunNextTask();
@@ -376,7 +387,10 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) {
EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultInitialTimeoutSecs),
runner_->GetPostedTasks().front().delay);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
// After we run the next task, we should close the connection.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
@@ -403,7 +417,7 @@ TEST_F(QuicConnectionHelperTest, WritePacketToWireAsync) {
AddWrite(ASYNC, ConstructClosePacket(1, 0));
Initialize();
- EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
int error = 0;
EXPECT_EQ(-1, helper_->WritePacketToWire(*GetWrite(0), &error));
EXPECT_EQ(ERR_IO_PENDING, error);
@@ -423,7 +437,8 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// kDefaultInitialTimeoutSecs.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000));
EXPECT_EQ(5000u, clock_.ApproximateNow().Subtract(start).ToMicroseconds());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_,
+ SentPacket(_, 1, _, NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
// Send an ack so we don't set the retransmission alarm.
connection_->SendAck();
@@ -439,7 +454,10 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
runner_->RunNextTask();
EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
clock_.ApproximateNow().Subtract(
@@ -454,23 +472,26 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) {
// Test that if we send a packet with a delay, it ends up queued.
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
+ Return(QuicTime::Delta::Zero()));
EXPECT_CALL(
*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ Return(QuicTime::Delta::FromMicroseconds(1)));
QuicPacket* packet = ConstructRawDataPacket(1);
- connection_->SendOrQueuePacket(
- ENCRYPTION_NONE, 1, packet, 0, HAS_RETRANSMITTABLE_DATA);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ connection_->SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0,
+ HAS_RETRANSMITTABLE_DATA,
+ QuicConnection::NO_FORCE);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ _));
EXPECT_EQ(1u, connection_->NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
- testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
runner_->RunNextTask();
EXPECT_EQ(0u, connection_->NumQueuedPackets());
EXPECT_TRUE(AtEof());
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 2221b50123..5195f4391a 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -12,6 +12,8 @@
#include "net/base/net_log.h"
#include "net/quic/crypto/crypto_handshake.h"
+using std::string;
+
namespace net {
namespace {
@@ -416,4 +418,11 @@ void QuicConnectionLogger::OnConnectionClose(QuicErrorCode error,
base::Bind(&NetLogQuicConnectionClosedCallback, error, from_peer));
}
+void QuicConnectionLogger::OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) {
+ string quic_version = QuicVersionToString(version);
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_VERSION_NEGOTIATED,
+ NetLog::StringCallback("version", &quic_version));
+}
+
} // namespace net
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h
index f9080d643e..d498b128bd 100644
--- a/net/quic/quic_connection_logger.h
+++ b/net/quic/quic_connection_logger.h
@@ -56,6 +56,7 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger
void OnCryptoHandshakeMessageSent(
const CryptoHandshakeMessage& message);
void OnConnectionClose(QuicErrorCode error, bool from_peer);
+ void OnSuccessfulVersionNegotiation(const QuicVersion& version);
private:
BoundNetLog net_log_;
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index e57e7aea19..afe8a7663b 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -389,6 +389,40 @@ class TestConnection : public QuicConnection {
QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
}
+ bool SendOrQueuePacket(EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ HasRetransmittableData retransmittable) {
+ return SendOrQueuePacket(level,
+ sequence_number,
+ packet,
+ entropy_hash,
+ retransmittable,
+ NO_FORCE);
+ }
+
+ QuicConsumedData SendStreamData(QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return SendvStreamData(id, &iov, 1, offset, fin);
+ }
+
+ QuicConsumedData SendStreamDataAndNotifyWhenAcked(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return SendvStreamDataAndNotifyWhenAcked(id, &iov, 1, offset, fin,
+ delegate);
+ }
+
QuicConsumedData SendStreamData3() {
return SendStreamData(kStreamId3, "food", 0, !kFin);
}
@@ -403,7 +437,11 @@ class TestConnection : public QuicConnection {
// split needlessly across packet boundaries). As a result, we have separate
// tests for some cases for this stream.
QuicConsumedData SendCryptoStreamData() {
- return SendStreamData(kCryptoStreamId, "chlo", 0, !kFin);
+ this->Flush();
+ QuicConsumedData consumed =
+ SendStreamData(kCryptoStreamId, "chlo", 0, !kFin);
+ this->Flush();
+ return consumed;
}
bool is_server() {
@@ -467,15 +505,18 @@ class QuicConnectionTest : public ::testing::Test {
QuicTime::Delta::Zero()));
EXPECT_CALL(*receive_algorithm_,
RecordIncomingPacket(_, _, _, _)).Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(Return(
QuicBandwidth::FromKBitsPerSecond(100)));
EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(Return(
QuicTime::Delta::FromMilliseconds(100)));
+ ON_CALL(*send_algorithm_, SentPacket(_, _, _, _, _))
+ .WillByDefault(Return(true));
// TODO(rch): remove this.
QuicConnection::g_acks_do_not_instigate_acks = true;
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
}
~QuicConnectionTest() {
@@ -513,8 +554,7 @@ class QuicConnectionTest : public ::testing::Test {
}
void ProcessPacket(QuicPacketSequenceNumber number) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _))
- .WillOnce(Return(accept_packet_));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
ProcessDataPacket(number, 0, !kEntropyFlag);
}
@@ -562,10 +602,9 @@ class QuicConnectionTest : public ::testing::Test {
size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
bool expect_revival, bool entropy_flag) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll(
- SaveArg<2>(&revived_header_), Return(accept_packet_)));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
}
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(accept_packet_))
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_))
.RetiresOnSaturation();
return ProcessDataPacket(number, 1, entropy_flag);
}
@@ -578,8 +617,7 @@ class QuicConnectionTest : public ::testing::Test {
bool entropy_flag,
QuicPacket* packet) {
if (expect_revival) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll(
- SaveArg<2>(&revived_header_), Return(accept_packet_)));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
}
// Construct the decrypted data packet so we can compute the correct
@@ -630,21 +668,21 @@ class QuicConnectionTest : public ::testing::Test {
QuicStreamOffset offset, bool fin,
QuicPacketSequenceNumber* last_packet) {
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce(
- SaveArg<2>(&packet_size));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&packet_size), Return(true)));
connection_.SendStreamData(id, data, offset, fin);
if (last_packet != NULL) {
*last_packet =
QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number();
}
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber());
return packet_size;
}
void SendAckPacketToPeer() {
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
connection_.SendAck();
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber());
}
QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame,
@@ -730,7 +768,6 @@ class QuicConnectionTest : public ::testing::Test {
StrictMock<MockConnectionVisitor> visitor_;
QuicPacketHeader header_;
- QuicPacketHeader revived_header_;
QuicStreamFrame frame1_;
QuicStreamFrame frame2_;
scoped_ptr<QuicAckFrame> outgoing_ack_;
@@ -741,6 +778,8 @@ class QuicConnectionTest : public ::testing::Test {
};
TEST_F(QuicConnectionTest, PacketsInOrder) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(1);
EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed);
EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
@@ -755,6 +794,8 @@ TEST_F(QuicConnectionTest, PacketsInOrder) {
}
TEST_F(QuicConnectionTest, PacketsRejected) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(1);
EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed);
EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size());
@@ -767,6 +808,8 @@ TEST_F(QuicConnectionTest, PacketsRejected) {
}
TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(3);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
@@ -784,13 +827,15 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrder) {
}
TEST_F(QuicConnectionTest, DuplicatePacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(3);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
EXPECT_TRUE(IsMissing(1));
// Send packet 3 again, but do not set the expectation that
- // the visitor OnPacket() will be called.
+ // the visitor OnStreamFrames() will be called.
ProcessDataPacket(3, 0, !kEntropyFlag);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
@@ -798,6 +843,8 @@ TEST_F(QuicConnectionTest, DuplicatePacket) {
}
TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(3);
EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed);
EXPECT_TRUE(IsMissing(2));
@@ -833,7 +880,7 @@ TEST_F(QuicConnectionTest, RejectPacketTooFarOut) {
}
TEST_F(QuicConnectionTest, TruncatedAck) {
- EXPECT_CALL(visitor_, OnAck(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
for (int i = 0; i < 200; ++i) {
@@ -861,6 +908,8 @@ TEST_F(QuicConnectionTest, TruncatedAck) {
}
TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessPacket(1);
// Delay sending, then queue up an ack.
EXPECT_CALL(*send_algorithm_,
@@ -880,11 +929,13 @@ TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
}
TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber largest_observed;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION))
- .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size)));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
+ .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size),
+ Return(true)));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1);
connection_.SendStreamData(1, "foo", 0, !kFin);
QuicAckFrame frame(1, QuicTime::Zero(), largest_observed);
@@ -895,13 +946,13 @@ TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) {
ProcessAckPacket(&frame, true);
// Third nack should retransmit the largest observed packet.
EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize,
- IS_RETRANSMISSION));
+ IS_RETRANSMISSION, _));
ProcessAckPacket(&frame, true);
// Now if the peer sends an ack which still reports the retransmitted packet
// as missing, then that will count as a packet which instigates an ack.
ProcessAckPacket(&frame, true);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _));
ProcessAckPacket(&frame, true);
// But an ack with no new missing packest will not send an ack.
@@ -911,6 +962,8 @@ TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) {
}
TEST_F(QuicConnectionTest, LeastUnackedLower) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
@@ -930,12 +983,14 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) {
// Now claim it's one, but set the ordering so it was sent "after" the first
// one. This should cause a connection error.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
creator_.set_sequence_number(7);
ProcessAckPacket(&frame2, false);
}
TEST_F(QuicConnectionTest, LargestObservedLower) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
@@ -945,7 +1000,6 @@ TEST_F(QuicConnectionTest, LargestObservedLower) {
QuicAckFrame frame(2, QuicTime::Zero(), 0);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
&connection_, 2);
- EXPECT_CALL(visitor_, OnAck(_));
ProcessAckPacket(&frame, true);
// Now change it to 1, and it should cause a connection error.
@@ -955,8 +1009,9 @@ TEST_F(QuicConnectionTest, LargestObservedLower) {
}
TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
// Create an ack with least_unacked is 2 in packet number 1.
creator_.set_sequence_number(0);
QuicAckFrame frame(0, QuicTime::Zero(), 2);
@@ -965,12 +1020,14 @@ TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) {
TEST_F(QuicConnectionTest,
NackSequenceNumberGreaterThanLargestReceived) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL);
SendStreamDataToPeer(1, "bar", 3, !kFin, NULL);
SendStreamDataToPeer(1, "eep", 6, !kFin, NULL);
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
QuicAckFrame frame(0, QuicTime::Zero(), 1);
frame.received_info.missing_packets.insert(3);
ProcessAckPacket(&frame, false);
@@ -979,12 +1036,14 @@ TEST_F(QuicConnectionTest,
TEST_F(QuicConnectionTest, AckUnsentData) {
// Ack a packet which has not been sent.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
QuicAckFrame frame(1, QuicTime::Zero(), 0);
ProcessAckPacket(&frame, false);
}
TEST_F(QuicConnectionTest, AckAll) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
creator_.set_sequence_number(1);
@@ -1093,6 +1152,7 @@ TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) {
}
TEST_F(QuicConnectionTest, BasicSending) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6);
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
@@ -1109,11 +1169,7 @@ TEST_F(QuicConnectionTest, BasicSending) {
SendAckPacketToPeer(); // Packet 5
EXPECT_EQ(1u, last_ack()->sent_info.least_unacked);
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
-
// Peer acks up to packet 3.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame(3, QuicTime::Zero(), 0);
frame.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 3);
@@ -1124,11 +1180,7 @@ TEST_F(QuicConnectionTest, BasicSending) {
// ack for 4.
EXPECT_EQ(4u, last_ack()->sent_info.least_unacked);
- expected_acks.clear();
- expected_acks.insert(4);
-
// Peer acks up to packet 4, the last packet.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
QuicAckFrame frame2(6, QuicTime::Zero(), 0);
frame2.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
@@ -1162,7 +1214,7 @@ TEST_F(QuicConnectionTest, FECSending) {
connection_.options()->max_packets_per_fec_group = 2;
// Send 4 data packets and 2 FEC packets.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(6);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(6);
// The first stream frame will consume 2 fewer bytes than the other three.
const string payload(payload_length * 4 - 6, 'a');
connection_.SendStreamData(1, payload, 0, !kFin);
@@ -1192,7 +1244,7 @@ TEST_F(QuicConnectionTest, FECQueueing) {
TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) {
connection_.options()->max_packets_per_fec_group = 1;
// 1 Data and 1 FEC packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(2);
connection_.SendStreamData(1, "foo", 0, !kFin);
// Larger timeout for FEC bytes to expire.
@@ -1201,7 +1253,7 @@ TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) {
clock_.AdvanceTime(retransmission_time);
// Send only data packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
// Abandon both FEC and data packet.
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2);
@@ -1209,12 +1261,13 @@ TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) {
}
TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
connection_.options()->max_packets_per_fec_group = 1;
const QuicPacketSequenceNumber sequence_number =
QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number() + 1;
// 1 Data and 1 FEC packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(2);
connection_.SendStreamData(1, "foo", 0, !kFin);
QuicAckFrame ack_fec(2, QuicTime::Zero(), 1);
@@ -1224,7 +1277,6 @@ TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
@@ -1235,7 +1287,7 @@ TEST_F(QuicConnectionTest, DontAbandonAckedFEC) {
// Abandon only data packet, FEC has been acked.
EXPECT_CALL(*send_algorithm_, AbandoningPacket(sequence_number, _)).Times(1);
// Send only data packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
connection_.OnRetransmissionTimeout();
}
@@ -1256,7 +1308,7 @@ TEST_F(QuicConnectionTest, FramePacking) {
// Unblock the connection.
connection_.GetSendAlarm()->Cancel();
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION))
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _))
.Times(1);
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1289,7 +1341,7 @@ TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
// Unblock the connection.
connection_.GetSendAlarm()->Cancel();
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION))
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _))
.Times(2);
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1320,7 +1372,7 @@ TEST_F(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
// Unblock the connection.
connection_.GetSendAlarm()->Cancel();
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION))
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _))
.Times(3);
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1352,7 +1404,7 @@ TEST_F(QuicConnectionTest, FramePackingFEC) {
// Unblock the connection.
connection_.GetSendAlarm()->Cancel();
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION)).Times(2);
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _)).Times(2);
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
EXPECT_FALSE(connection_.HasQueuedData());
@@ -1362,6 +1414,76 @@ TEST_F(QuicConnectionTest, FramePackingFEC) {
EXPECT_EQ(0u, helper_->frame_count());
}
+TEST_F(QuicConnectionTest, FramePackingSendv) {
+ // Send two stream frames in 1 packet by using writev.
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _));
+
+ char data[] = "ABCD";
+ iovec iov[2] = { {static_cast<void*>(data), 2},
+ {static_cast<void*>(data + 2), 2} };
+ connection_.SendvStreamData(1, iov, 2, 0, !kFin);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's two stream frames from one stream.
+ // TODO(ianswett): Ideally this would arrive in one frame in the future.
+ EXPECT_EQ(2u, helper_->frame_count());
+ EXPECT_EQ(2u, helper_->stream_frames()->size());
+ EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id);
+ EXPECT_EQ(1u, (*helper_->stream_frames())[1].stream_id);
+}
+
+TEST_F(QuicConnectionTest, FramePackingSendvQueued) {
+ // Try to send two stream frames in 1 packet by using writev.
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _));
+
+ helper_->set_blocked(true);
+ char data[] = "ABCD";
+ iovec iov[2] = { {static_cast<void*>(data), 2},
+ {static_cast<void*>(data + 2), 2} };
+ connection_.SendvStreamData(1, iov, 2, 0, !kFin);
+
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ // Attempt to send all packets, but since we're actually still
+ // blocked, they should all remain queued.
+ EXPECT_FALSE(connection_.OnCanWrite());
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Unblock the writes and actually send.
+ helper_->set_blocked(false);
+ EXPECT_CALL(visitor_, OnCanWrite());
+ EXPECT_TRUE(connection_.OnCanWrite());
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+
+ // Parse the last packet and ensure it's two stream frames from one stream.
+ // TODO(ianswett): Ideally this would arrive in one frame in the future.
+ EXPECT_EQ(2u, helper_->frame_count());
+ EXPECT_EQ(2u, helper_->stream_frames()->size());
+ EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id);
+ EXPECT_EQ(1u, (*helper_->stream_frames())[1].stream_id);
+}
+
+TEST_F(QuicConnectionTest, SendingZeroBytes) {
+ // Send a zero byte write with a fin using writev.
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _));
+
+ iovec iov[1];
+ connection_.SendvStreamData(1, iov, 0, 0, kFin);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's two stream frames from one stream.
+ // TODO(ianswett): Ideally this would arrive in one frame in the future.
+ EXPECT_EQ(1u, helper_->frame_count());
+ EXPECT_EQ(1u, helper_->stream_frames()->size());
+ EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id);
+ EXPECT_TRUE((*helper_->stream_frames())[0].fin);
+}
+
TEST_F(QuicConnectionTest, OnCanWrite) {
// Visitor's OnCanWill send data, but will return false.
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
@@ -1396,9 +1518,7 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Peer acks one but not two or three. Right now we only retransmit on
// explicit nack, so it should not trigger a retransimission.
@@ -1409,10 +1529,6 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
ProcessAckPacket(&ack_one, true);
ProcessAckPacket(&ack_one, true);
- expected_acks.clear();
- expected_acks.insert(3);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
-
// Peer acks up to 3 with two explicitly missing. Two nacks should cause no
// change.
QuicAckFrame nack_two(3, QuicTime::Zero(), 0);
@@ -1427,16 +1543,18 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) {
// The third nack should trigger a retransimission.
EXPECT_CALL(*send_algorithm_,
SentPacket(_, _, second_packet_size - kQuicVersionSize,
- IS_RETRANSMISSION)).Times(1);
+ IS_RETRANSMISSION, _)).Times(1);
ProcessAckPacket(&nack_two, true);
}
TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
QuicPacketSequenceNumber largest_observed;
QuicByteCount packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION))
- .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size)));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
+ .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size),
+ Return(true)));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1);
connection_.SendStreamData(1, "foo", 0, !kFin);
QuicAckFrame frame(1, QuicTime::Zero(), largest_observed);
@@ -1447,13 +1565,13 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) {
ProcessAckPacket(&frame, true);
// Third nack should retransmit the largest observed packet.
EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize,
- IS_RETRANSMISSION));
+ IS_RETRANSMISSION, _));
ProcessAckPacket(&frame, true);
}
TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
for (int i = 0; i < 200; ++i) {
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
connection_.SendStreamData(1, "foo", i * 3, !kFin);
}
@@ -1467,7 +1585,7 @@ TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessAckPacket(&frame, true);
EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_));
@@ -1475,7 +1593,7 @@ TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
clock_.AdvanceTime(DefaultRetransmissionTime());
// Only packets that are less than largest observed should be retransmitted.
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(192);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(192);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(192);
connection_.OnRetransmissionTimeout();
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
@@ -1483,11 +1601,12 @@ TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) {
// Retransmit already retransmitted packets event though the sequence number
// greater than the largest observed.
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(192);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(192);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(192);
connection_.OnRetransmissionTimeout();
}
TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(11);
@@ -1507,19 +1626,16 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) {
nack.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 12) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 11);
- SequenceNumberSet expected_acks;
- expected_acks.insert(12);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
// Nack three times.
ProcessAckPacket(&nack, true);
ProcessAckPacket(&nack, true);
// The third call should trigger retransmitting 10 packets.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(10);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(10);
ProcessAckPacket(&nack, true);
// The fourth call should trigger retransmitting the 11th packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
ProcessAckPacket(&nack, true);
}
@@ -1548,26 +1664,15 @@ TEST_F(QuicConnectionTest, MultipleAcks) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 1);
- // The connection should pass up acks for 1, 4, 5. 2 is not acked, and 3 was
- // an ackframe so should not be passed up.
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- expected_acks.insert(4);
- expected_acks.insert(5);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
ProcessAckPacket(&frame1, true);
// Now the client implicitly acks 2, and explicitly acks 6
QuicAckFrame frame2(6, QuicTime::Zero(), 0);
frame2.received_info.entropy_hash =
QuicConnectionPeer::GetSentEntropyHash(&connection_, 6);
- expected_acks.clear();
- // Both acks should be passed up.
- expected_acks.insert(2);
- expected_acks.insert(6);
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
ProcessAckPacket(&frame2, true);
}
@@ -1576,12 +1681,7 @@ TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1;
SendAckPacketToPeer(); // Packet 2
- // This sets least unacked to 3 (unsent packet), since we don't need
- // an ack for Packet 2 (ack packet).
- SequenceNumberSet expected_acks;
- expected_acks.insert(1);
- // Peer acks packet 1.
- EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks)));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicAckFrame frame(1, QuicTime::Zero(), 0);
frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(
&connection_, 1);
@@ -1606,42 +1706,57 @@ TEST_F(QuicConnectionTest, DontLatchUnackedPacket) {
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
// Don't send missing packet 1.
ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL);
- EXPECT_FALSE(revived_header_.entropy_flag);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessFecProtectedPacket(1, false, kEntropyFlag);
// Don't send missing packet 2.
ProcessFecPacket(3, 1, true, !kEntropyFlag, NULL);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should not be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessFecProtectedPacket(1, false, !kEntropyFlag);
// Don't send missing packet 2.
ProcessFecProtectedPacket(3, false, !kEntropyFlag);
ProcessFecPacket(4, 1, true, kEntropyFlag, NULL);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should not be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
// Don't send missing packet 1.
ProcessFecPacket(3, 1, false, !kEntropyFlag, NULL);
// out of order
ProcessFecProtectedPacket(2, true, !kEntropyFlag);
- EXPECT_FALSE(revived_header_.entropy_flag);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
ProcessFecProtectedPacket(1, false, !kEntropyFlag);
// Don't send missing packet 2.
ProcessFecPacket(6, 1, false, kEntropyFlag, NULL);
ProcessFecProtectedPacket(3, false, kEntropyFlag);
ProcessFecProtectedPacket(4, false, kEntropyFlag);
ProcessFecProtectedPacket(5, true, !kEntropyFlag);
- EXPECT_TRUE(revived_header_.entropy_flag);
+ // Entropy flag should be true, so entropy should be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
}
TEST_F(QuicConnectionTest, TestRetransmit) {
@@ -1655,7 +1770,7 @@ TEST_F(QuicConnectionTest, TestRetransmit) {
connection_.GetRetransmissionAlarm()->deadline());
// Simulate the retransimission alarm firing
clock_.AdvanceTime(DefaultRetransmissionTime());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1);
connection_.RetransmitPacket(1);
EXPECT_EQ(2u, last_header()->packet_sequence_number);
@@ -1684,12 +1799,12 @@ TEST_F(QuicConnectionTest, RetransmitWithSameEncryptionLevel) {
clock_.AdvanceTime(DefaultRetransmissionTime());
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
connection_.RetransmitPacket(1);
// Packet should have been sent with ENCRYPTION_NONE.
EXPECT_EQ(0x01010101u, final_bytes_of_last_packet());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
connection_.RetransmitPacket(2);
// Packet should have been sent with ENCRYPTION_INITIAL.
EXPECT_EQ(0x02020202u, final_bytes_of_last_packet());
@@ -1706,7 +1821,7 @@ TEST_F(QuicConnectionTest,
new TaggingEncrypter(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(0);
EXPECT_CALL(*send_algorithm_, AbandoningPacket(sequence_number, _)).Times(1);
QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
@@ -1731,13 +1846,14 @@ TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
SendStreamDataToPeer(2, "bar", 0, !kFin, NULL);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(1);
connection_.RetransmitUnackedPackets(QuicConnection::INITIAL_ENCRYPTION_ONLY);
}
TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
use_tagging_decrypter();
const uint8 tag = 0x07;
@@ -1753,26 +1869,26 @@ TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) {
connection_.SetDecrypter(new StrictTaggingDecrypter(tag));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly(
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2).WillRepeatedly(
Return(true));
ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL);
// Finally, process a third packet and note that we do not
// reprocess the buffered packet.
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true));
ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL);
}
TEST_F(QuicConnectionTest, TestRetransmitOrder) {
QuicByteCount first_packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce(
- SaveArg<2>(&first_packet_size));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).WillOnce(
+ DoAll(SaveArg<2>(&first_packet_size), Return(true)));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2);
connection_.SendStreamData(1, "first_packet", 0, !kFin);
QuicByteCount second_packet_size;
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce(
- SaveArg<2>(&second_packet_size));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).WillOnce(
+ DoAll(SaveArg<2>(&second_packet_size), Return(true)));
connection_.SendStreamData(1, "second_packet", 12, !kFin);
EXPECT_NE(first_packet_size, second_packet_size);
// Advance the clock by huge time to make sure packets will be retransmitted.
@@ -1780,20 +1896,20 @@ TEST_F(QuicConnectionTest, TestRetransmitOrder) {
{
InSequence s;
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, first_packet_size, _));
+ SentPacket(_, _, first_packet_size, _, _));
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, second_packet_size, _));
+ SentPacket(_, _, second_packet_size, _, _));
}
connection_.OnRetransmissionTimeout();
}
TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2);
QuicPacketSequenceNumber original_sequence_number;
- EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION))
- .WillOnce(SaveArg<1>(&original_sequence_number));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
+ .WillOnce(DoAll(SaveArg<1>(&original_sequence_number), Return(true)));
connection_.SendStreamData(1, "foo", 0, !kFin);
EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -1802,9 +1918,8 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
// Force retransmission due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
QuicPacketSequenceNumber rto_sequence_number;
- EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, IS_RETRANSMISSION))
- .WillOnce(SaveArg<1>(&rto_sequence_number));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION, _))
+ .WillOnce(DoAll(SaveArg<1>(&rto_sequence_number), Return(true)));
connection_.OnRetransmissionTimeout();
EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
&connection_, original_sequence_number));
@@ -1816,10 +1931,10 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) {
QuicPacketSequenceNumber nack_sequence_number;
// Ack packets might generate some other packets, which are not
// retransmissions. (More ack packets).
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION))
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
.Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION))
- .WillOnce(SaveArg<1>(&nack_sequence_number));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION, _))
+ .WillOnce(DoAll(SaveArg<1>(&nack_sequence_number), Return(true)));
QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0);
// Ack the retransmitted packet.
ack.received_info.missing_packets.insert(rto_sequence_number);
@@ -1849,6 +1964,45 @@ TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
}
+TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION, _))
+ .Times(2);
+ connection_.SendStreamData(1, "foo", 0, !kFin);
+ connection_.SendStreamData(2, "bar", 0, !kFin);
+ EXPECT_EQ(2u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Advance the time right before the RTO, then receive an ack for the first
+ // packet to delay the RTO.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_EQ(2u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+ EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
+ QuicAckFrame ack(1, QuicTime::Zero(), 0);
+ ProcessAckPacket(&ack, true);
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Move forward past the original RTO and ensure the RTO is still pending.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+
+ // Ensure the second packet gets retransmitted when it finally fires.
+ EXPECT_TRUE(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->IsSet());
+ EXPECT_GE(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->deadline(),
+ clock_.ApproximateNow());
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_LT(
+ QuicConnectionPeer::GetRetransmissionAlarm(&connection_)->deadline(),
+ clock_.ApproximateNow());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION, _));
+ EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _));
+ connection_.OnRetransmissionTimeout();
+
+ // The new retransmitted sequence number should now be in the timeout queue.
+ EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_));
+}
+
TEST_F(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
helper_->set_blocked(true);
@@ -1868,6 +2022,7 @@ TEST_F(QuicConnectionTest, TestQueued) {
}
TEST_F(QuicConnectionTest, CloseFecGroup) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
// Don't send missing packet 1
// Don't send missing packet 2
ProcessFecProtectedPacket(3, false, !kEntropyFlag);
@@ -1900,10 +2055,12 @@ TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
}
TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
SendAckPacketToPeer();
// Process an FEC packet, and revive the missing data packet
// but only contact the receive_algorithm once.
@@ -1914,7 +2071,7 @@ TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
TEST_F(QuicConnectionTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
QuicTime default_timeout = clock_.ApproximateNow().Add(
QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs));
@@ -1953,7 +2110,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)),
clock_.ApproximateNow());
@@ -1968,7 +2125,7 @@ TEST_F(QuicConnectionTest, SendScheduler) {
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
connection_.SendOrQueuePacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -1980,7 +2137,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelay) {
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
testing::Return(QuicTime::Delta::FromMicroseconds(1)));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, _)).Times(0);
connection_.SendOrQueuePacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -1991,7 +2148,7 @@ TEST_F(QuicConnectionTest, SendSchedulerForce) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, IS_RETRANSMISSION, _, _)).Times(0);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
connection_.SendOrQueuePacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
// XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce);
@@ -2004,7 +2161,7 @@ TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) {
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, _)).Times(0);
connection_.SendOrQueuePacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
@@ -2027,7 +2184,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) {
testing::Return(QuicTime::Delta::Zero()));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
connection_.GetSendAlarm()->Cancel();
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _));
EXPECT_CALL(visitor_, OnCanWrite());
connection_.OnCanWrite();
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -2038,7 +2195,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
.WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1);
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ SentPacket(_, 1, _, NOT_RETRANSMISSION, _));
connection_.SendStreamData(1, "foo", 0, !kFin);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
// Advance the time for retransmission of lost packet.
@@ -2058,7 +2215,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) {
// Ensure the scheduler is notified this is a retransmit.
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, IS_RETRANSMISSION));
+ SentPacket(_, _, _, IS_RETRANSMISSION, _));
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1));
connection_.GetSendAlarm()->Cancel();
EXPECT_CALL(visitor_, OnCanWrite());
@@ -2083,6 +2240,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) {
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
@@ -2098,7 +2256,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, _));
+ SentPacket(_, _, _, _, _));
ProcessAckPacket(&frame, true);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -2107,6 +2265,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) {
}
TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
@@ -2168,7 +2327,7 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
NOT_IN_FEC_GROUP, &payload_length);
// Queue the first packet.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(7);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(7);
// The first stream frame will consume 2 fewer bytes than the other six.
const string payload(payload_length * 7 - 12, 'a');
EXPECT_EQ(payload.size(),
@@ -2176,10 +2335,11 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) {
}
TEST_F(QuicConnectionTest, NoAckForClose) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(0);
EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(0);
ProcessClosePacket(2, 0);
}
@@ -2189,7 +2349,7 @@ TEST_F(QuicConnectionTest, SendWhenDisconnected) {
connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false);
EXPECT_FALSE(connection_.connected());
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, _)).Times(0);
connection_.SendOrQueuePacket(
ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
}
@@ -2207,6 +2367,8 @@ TEST_F(QuicConnectionTest, PublicReset) {
}
TEST_F(QuicConnectionTest, GoAway) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
QuicGoAwayFrame goaway;
goaway.last_good_stream_id = 1;
goaway.error_code = QUIC_PEER_GOING_AWAY;
@@ -2219,12 +2381,14 @@ TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
QuicAckFrame ack(0, QuicTime::Zero(), 4);
// Set the sequence number of the ack packet to be least unacked (4)
creator_.set_sequence_number(3);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessAckPacket(&ack, true);
EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty());
}
TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(4, 1, kEntropyFlag);
ProcessDataPacket(3, 1, !kEntropyFlag);
@@ -2233,7 +2397,8 @@ TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) {
}
TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, kEntropyFlag);
ProcessDataPacket(4, 1, !kEntropyFlag);
@@ -2253,7 +2418,8 @@ TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
}
TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessDataPacket(1, 1, kEntropyFlag);
ProcessDataPacket(5, 1, !kEntropyFlag);
ProcessDataPacket(22, 1, kEntropyFlag);
@@ -2272,7 +2438,8 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
}
TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
QuicPacketEntropyHash entropy[51];
entropy[0] = 0;
for (int i = 1; i < 51; ++i) {
@@ -2396,7 +2563,8 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) {
scoped_ptr<QuicPacket> packet(
framer_.BuildUnsizedDataPacket(header, frames).packet);
encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
ASSERT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(
@@ -2431,18 +2599,18 @@ TEST_F(QuicConnectionTest, BadVersionNegotiation) {
TEST_F(QuicConnectionTest, CheckSendStats) {
EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(3);
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION));
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _));
connection_.SendStreamData(1u, "first", 0, !kFin);
size_t first_packet_size = last_sent_packet_size();
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, NOT_RETRANSMISSION));
+ SentPacket(_, _, _, NOT_RETRANSMISSION, _));
connection_.SendStreamData(1u, "second", 0, !kFin);
size_t second_packet_size = last_sent_packet_size();
// 2 retransmissions due to rto, 1 due to explicit nack.
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, _, _, IS_RETRANSMISSION)).Times(3);
+ SentPacket(_, _, _, IS_RETRANSMISSION, _)).Times(3);
// Retransmit due to RTO.
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
@@ -2456,10 +2624,10 @@ TEST_F(QuicConnectionTest, CheckSendStats) {
QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^
QuicConnectionPeer::GetSentEntropyHash(&connection_, 2);
QuicFrame frame(&nack_three);
- EXPECT_CALL(visitor_, OnAck(_));
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
EXPECT_CALL(visitor_, OnCanWrite()).Times(3).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessFramePacket(frame);
ProcessFramePacket(frame);
@@ -2481,6 +2649,8 @@ TEST_F(QuicConnectionTest, CheckSendStats) {
}
TEST_F(QuicConnectionTest, CheckReceiveStats) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
size_t received_bytes = 0;
received_bytes += ProcessFecProtectedPacket(1, false, !kEntropyFlag);
received_bytes += ProcessFecProtectedPacket(3, false, !kEntropyFlag);
@@ -2549,7 +2719,8 @@ TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) {
ENCRYPTION_NONE, 1, *packet));
EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true));
- EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(0);
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(0);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
}
@@ -2622,6 +2793,8 @@ TEST_F(QuicConnectionTest, ConnectionCloseWhenNothingPending) {
}
TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
// Create a delegate which we expect to be called.
MockAckNotifierDelegate delegate;
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
@@ -2630,18 +2803,18 @@ TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) {
connection_.SendStreamDataAndNotifyWhenAcked(1, "foo", 0, !kFin, &delegate);
// Process an ACK from the server which should trigger the callback.
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
QuicAckFrame frame(1, QuicTime::Zero(), 0);
ProcessAckPacket(&frame, true);
}
TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
// Create a delegate which we don't expect to be called.
MockAckNotifierDelegate delegate;
EXPECT_CALL(delegate, OnAckNotification()).Times(0);;
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2);
EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1);
@@ -2661,13 +2834,12 @@ TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
}
TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
// Create a delegate which we expect to be called.
MockAckNotifierDelegate delegate;
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
- // OnAck called twice: once with missing packet, once after retransmit.
- EXPECT_CALL(visitor_, OnAck(_)).Times(2);
-
// In total expect ACKs for all 4 packets.
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(4);
@@ -2688,7 +2860,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
// Advance time to trigger RTO, for packet 2 (which should be retransmitted as
// packet 5).
EXPECT_CALL(*send_algorithm_, AbandoningPacket(2, _)).Times(1);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(1);
clock_.AdvanceTime(DefaultRetransmissionTime());
connection_.OnRetransmissionTimeout();
@@ -2702,6 +2874,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
// TODO(rjshade): Add a similar test that FEC recovery on peer (and resulting
// ACK) triggers notification on our end.
TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(visitor_, OnCanWrite()).Times(1).WillOnce(Return(true));
// Create a delegate which we expect to be called.
@@ -2709,7 +2882,6 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
EXPECT_CALL(delegate, OnAckNotification()).Times(1);;
// Expect ACKs for 1 packet.
- EXPECT_CALL(visitor_, OnAck(_)).Times(1);
EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1);
// Send one packet, and register to be notified on ACK.
@@ -2723,8 +2895,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
frames.push_back(QuicFrame(&ack_frame));
// Dummy stream frame to satisfy expectations set elsewhere.
- QuicFrame frame(&frame1_);
- frames.push_back(frame);
+ frames.push_back(QuicFrame(&frame1_));
QuicPacketHeader ack_header;
ack_header.public_header.guid = guid_;
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
index e43406fbc3..b0f81174b7 100644
--- a/net/quic/quic_crypto_client_stream.cc
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -264,7 +264,6 @@ void QuicCryptoClientStream::DoHandshakeLoop(
verify_ok_ = false;
ProofVerifier::Status status = verifier->VerifyProof(
- session()->connection()->version(),
server_hostname_,
cached->server_config(),
cached->certs(),
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
index a23a34d5b4..63637f5429 100644
--- a/net/quic/quic_crypto_server_stream.cc
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -131,7 +131,6 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
string* error_details) {
return crypto_config_.ProcessClientHello(
message,
- session()->connection()->version(),
session()->connection()->guid(),
session()->connection()->peer_address(),
session()->connection()->clock(),
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
index 569648f1ba..3c10c5bfbf 100644
--- a/net/quic/quic_crypto_stream.cc
+++ b/net/quic/quic_crypto_stream.cc
@@ -59,8 +59,12 @@ void QuicCryptoStream::SendHandshakeMessage(
const CryptoHandshakeMessage& message) {
session()->OnCryptoHandshakeMessageSent(message);
const QuicData& data = message.GetSerialized();
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // any other frames in a single packet.
+ session()->connection()->Flush();
// TODO(wtc): check the return value.
WriteData(string(data.data(), data.length()), false);
+ session()->connection()->Flush();
}
const QuicCryptoNegotiatedParameters&
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 4160ddcb94..cfc8c95774 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -1040,7 +1040,7 @@ bool QuicFramer::ProcessFrameData() {
// TODO(jri): Retain this else block when support for
// QUIC version < 10 removed. Remove above if block.
- // Special frame type processing for QUIC version >= 10
+ // Special frame type processing for QUIC version >= 10.
if (frame_type & kQuicFrameTypeSpecialMask) {
// Stream Frame
if (frame_type & kQuicFrameTypeStreamMask) {
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index a91b9e2658..0122c64b1a 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -11,6 +11,7 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_reliable_client_stream.h"
#include "net/quic/quic_utils.h"
#include "net/socket/next_proto.h"
@@ -29,6 +30,7 @@ QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession> session)
stream_(NULL),
request_info_(NULL),
request_body_stream_(NULL),
+ priority_(MINIMUM_PRIORITY),
response_info_(NULL),
response_status_(OK),
response_headers_received_(false),
@@ -52,6 +54,7 @@ int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
stream_net_log_ = stream_net_log;
request_info_ = request_info;
+ priority_ = priority;
int rv = stream_request_.StartRequest(
session_, &stream_, base::Bind(&QuicHttpStream::OnStreamReady,
@@ -82,6 +85,8 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
CHECK(!callback.is_null());
CHECK(response);
+ QuicPriority priority = ConvertRequestPriorityToQuicPriority(priority_);
+ stream_->set_priority(priority);
// Store the serialized request headers.
SpdyHeaderBlock headers;
CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers,
@@ -89,7 +94,8 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
if (session_->connection()->version() < QUIC_VERSION_9) {
request_ = stream_->compressor()->CompressHeaders(headers);
} else {
- request_ = stream_->compressor()->CompressHeadersWithPriority(0, headers);
+ request_ = stream_->compressor()->CompressHeadersWithPriority(priority,
+ headers);
}
// Log the actual request with the URL Request's net log.
stream_net_log_.AddEvent(
@@ -207,7 +213,7 @@ void QuicHttpStream::Close(bool not_reusable) {
stream_->SetDelegate(NULL);
// TODO(rch): use new CANCELLED error code here once quic 11
// is everywhere.
- stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ stream_->Close(QUIC_ERROR_PROCESSING_STREAM);
stream_ = NULL;
}
}
@@ -264,7 +270,7 @@ void QuicHttpStream::Drain(HttpNetworkSession* session) {
}
void QuicHttpStream::SetPriority(RequestPriority priority) {
- // Nothing to do here (yet).
+ priority_ = priority;
}
int QuicHttpStream::OnSendData() {
@@ -336,6 +342,10 @@ void QuicHttpStream::OnError(int error) {
DoCallback(response_status_);
}
+bool QuicHttpStream::HasSendHeadersComplete() {
+ return next_state_ > STATE_SEND_HEADERS_COMPLETE;
+}
+
void QuicHttpStream::OnIOComplete(int rv) {
rv = DoLoop(rv);
diff --git a/net/quic/quic_http_stream.h b/net/quic/quic_http_stream.h
index cc2b973e87..71fb515335 100644
--- a/net/quic/quic_http_stream.h
+++ b/net/quic/quic_http_stream.h
@@ -15,6 +15,10 @@
namespace net {
+namespace test {
+class QuicHttpStreamPeer;
+} // namespace test
+
// The QuicHttpStream is a QUIC-specific HttpStream subclass. It holds a
// non-owning pointer to a QuicReliableClientStream which it uses to
// send and receive data.
@@ -62,8 +66,11 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
virtual int OnDataReceived(const char* data, int length) OVERRIDE;
virtual void OnClose(QuicErrorCode error) OVERRIDE;
virtual void OnError(int error) OVERRIDE;
+ virtual bool HasSendHeadersComplete() OVERRIDE;
private:
+ friend class test::QuicHttpStreamPeer;
+
enum State {
STATE_NONE,
STATE_SEND_HEADERS,
@@ -106,6 +113,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream :
const HttpRequestInfo* request_info_;
// The request body to send, if any, owned by the caller.
UploadDataStream* request_body_stream_;
+ // The priority of the request.
+ RequestPriority priority_;
// |response_info_| is the HTTP response data object which is filled in
// when a the response headers are read. It is not owned by this stream.
HttpResponseInfo* response_info_;
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 6a584f1800..37242bf8cd 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -19,6 +19,8 @@
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_reliable_client_stream.h"
#include "net/quic/spdy_utils.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
@@ -31,6 +33,7 @@
#include "net/spdy/spdy_framer.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/write_blocked_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -102,6 +105,14 @@ class AutoClosingStream : public QuicHttpStream {
} // namespace
+class QuicHttpStreamPeer {
+ public:
+ static QuicReliableClientStream* GetQuicReliableClientStream(
+ QuicHttpStream* stream) {
+ return stream->stream_;
+ }
+};
+
class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
protected:
const static bool kFin = true;
@@ -177,7 +188,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
receive_algorithm_ = new TestReceiveAlgorithm(NULL);
EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).
Times(AnyNumber());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
Return(QuicTime::Delta::Zero()));
EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
@@ -206,14 +217,16 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
new QuicHttpStream(session_->GetWeakPtr()));
}
- void SetRequestString(const std::string& method, const std::string& path) {
+ void SetRequestString(const std::string& method,
+ const std::string& path,
+ RequestPriority priority) {
SpdyHeaderBlock headers;
headers[":method"] = method;
headers[":host"] = "www.google.com";
headers[":path"] = path;
headers[":scheme"] = "http";
headers[":version"] = "HTTP/1.1";
- request_data_ = SerializeHeaderBlock(headers, true);
+ request_data_ = SerializeHeaderBlock(headers, true, priority);
}
void SetResponseString(const std::string& status, const std::string& body) {
@@ -221,14 +234,17 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
headers[":status"] = status;
headers[":version"] = "HTTP/1.1";
headers["content-type"] = "text/plain";
- response_data_ = SerializeHeaderBlock(headers, false) + body;
+ response_data_ = SerializeHeaderBlock(headers, false, DEFAULT_PRIORITY) +
+ body;
}
std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers,
- bool write_priority) {
+ bool write_priority,
+ RequestPriority priority) {
QuicSpdyCompressor compressor;
if (framer_.version() >= QUIC_VERSION_9 && write_priority) {
- return compressor.CompressHeadersWithPriority(0, headers);
+ return compressor.CompressHeadersWithPriority(
+ ConvertRequestPriorityToQuicPriority(priority), headers);
}
return compressor.CompressHeaders(headers);
}
@@ -249,7 +265,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> {
QuicEncryptedPacket* ConstructRstStreamPacket(
QuicPacketSequenceNumber sequence_number) {
InitializeHeader(sequence_number, false);
- QuicRstStreamFrame frame(3, QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ QuicRstStreamFrame frame(3, QUIC_ERROR_PROCESSING_STREAM);
return ConstructPacket(header_, QuicFrame(&frame));
}
@@ -350,7 +366,7 @@ TEST_F(QuicHttpStreamTest, IsConnectionReusable) {
}
TEST_F(QuicHttpStreamTest, GetRequest) {
- SetRequestString("GET", "/");
+ SetRequestString("GET", "/", DEFAULT_PRIORITY);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
request_data_));
Initialize();
@@ -393,7 +409,7 @@ TEST_F(QuicHttpStreamTest, GetRequest) {
// Regression test for http://crbug.com/288128
TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
- SetRequestString("GET", "/");
+ SetRequestString("GET", "/", DEFAULT_PRIORITY);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0,
request_data_));
Initialize();
@@ -440,7 +456,7 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) {
}
TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) {
- SetRequestString("GET", "/");
+ SetRequestString("GET", "/", DEFAULT_PRIORITY);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
Initialize();
@@ -482,7 +498,7 @@ TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) {
}
TEST_F(QuicHttpStreamTest, SendPostRequest) {
- SetRequestString("POST", "/");
+ SetRequestString("POST", "/", DEFAULT_PRIORITY);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, kFin,
request_data_.length(),
@@ -539,7 +555,7 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) {
}
TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
- SetRequestString("POST", "/");
+ SetRequestString("POST", "/", DEFAULT_PRIORITY);
size_t chunk_size = strlen(kUploadData);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_));
AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, !kFin,
@@ -601,7 +617,7 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) {
}
TEST_F(QuicHttpStreamTest, DestroyedEarly) {
- SetRequestString("GET", "/");
+ SetRequestString("GET", "/", DEFAULT_PRIORITY);
AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
use_closing_stream_ = true;
@@ -613,9 +629,54 @@ TEST_F(QuicHttpStreamTest, DestroyedEarly) {
EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
net_log_, callback_.callback()));
EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
- callback_.callback()));
+ callback_.callback()));
+ EXPECT_EQ(&response_, stream_->GetResponseInfo());
+
+ // Ack the request.
+ scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
+ ProcessPacket(*ack);
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ // Send the response with a body.
+ SetResponseString("404 OK", "hello world!");
+ scoped_ptr<QuicEncryptedPacket> resp(
+ ConstructDataPacket(2, false, kFin, 0, response_data_));
+
+ // In the course of processing this packet, the QuicHttpStream close itself.
+ ProcessPacket(*resp);
+
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_F(QuicHttpStreamTest, Priority) {
+ SetRequestString("GET", "/", MEDIUM);
+ AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
+ AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
+ use_closing_stream_ = true;
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM,
+ net_log_, callback_.callback()));
+
+ // Check that priority is highest.
+ QuicReliableClientStream* reliable_stream =
+ QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
+ DCHECK(reliable_stream);
+ DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority),
+ reliable_stream->EffectivePriority());
+
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
EXPECT_EQ(&response_, stream_->GetResponseInfo());
+ // Check that priority has now dropped back to MEDIUM.
+ DCHECK_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority(
+ reliable_stream->EffectivePriority()));
+
// Ack the request.
scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0));
ProcessPacket(*ack);
diff --git a/net/quic/quic_http_utils.cc b/net/quic/quic_http_utils.cc
new file mode 100644
index 0000000000..4a48626854
--- /dev/null
+++ b/net/quic/quic_http_utils.cc
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_utils.h"
+
+namespace net {
+
+QuicPriority ConvertRequestPriorityToQuicPriority(
+ const RequestPriority priority) {
+ DCHECK_GE(priority, MINIMUM_PRIORITY);
+ DCHECK_LT(priority, NUM_PRIORITIES);
+ return static_cast<QuicPriority>(HIGHEST - priority);
+}
+
+NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
+ QuicPriority priority) {
+ // Handle invalid values gracefully.
+ return (priority >= 5) ?
+ IDLE : static_cast<RequestPriority>(HIGHEST - priority);
+}
+
+} // namespace net
diff --git a/net/quic/quic_http_utils.h b/net/quic/quic_http_utils.h
new file mode 100644
index 0000000000..c7e031ae60
--- /dev/null
+++ b/net/quic/quic_http_utils.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_HTTP_UTILS_H_
+#define NET_QUIC_QUIC_HTTP_UTILS_H_
+
+#include "net/base/net_export.h"
+#include "net/base/request_priority.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+NET_EXPORT_PRIVATE QuicPriority ConvertRequestPriorityToQuicPriority(
+ RequestPriority priority);
+
+NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
+ QuicPriority priority);
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HTTP_UTILS_H_
diff --git a/net/quic/quic_http_utils_test.cc b/net/quic/quic_http_utils_test.cc
new file mode 100644
index 0000000000..93b62e2e9d
--- /dev/null
+++ b/net/quic/quic_http_utils_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TEST(QuicHttpUtilsTest, ConvertRequestPriorityToQuicPriority) {
+ EXPECT_EQ(0u, ConvertRequestPriorityToQuicPriority(HIGHEST));
+ EXPECT_EQ(1u, ConvertRequestPriorityToQuicPriority(MEDIUM));
+ EXPECT_EQ(2u, ConvertRequestPriorityToQuicPriority(LOW));
+ EXPECT_EQ(3u, ConvertRequestPriorityToQuicPriority(LOWEST));
+ EXPECT_EQ(4u, ConvertRequestPriorityToQuicPriority(IDLE));
+}
+
+TEST(QuicHttpUtilsTest, ConvertQuicPriorityToRequestPriority) {
+ EXPECT_EQ(HIGHEST, ConvertQuicPriorityToRequestPriority(0));
+ EXPECT_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority(1));
+ EXPECT_EQ(LOW, ConvertQuicPriorityToRequestPriority(2));
+ EXPECT_EQ(LOWEST, ConvertQuicPriorityToRequestPriority(3));
+ EXPECT_EQ(IDLE, ConvertQuicPriorityToRequestPriority(4));
+ // These are invalid values, but we should still handle them
+ // gracefully. TODO(rtenneti): should we test for all possible values of
+ // uint32?
+ for (int i = 5; i < kuint8max; ++i) {
+ EXPECT_EQ(IDLE, ConvertQuicPriorityToRequestPriority(i));
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 801c7ef75e..a6cbff1b81 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -25,6 +25,7 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/quic_framer.h"
+#include "net/quic/quic_http_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
@@ -178,7 +179,8 @@ class QuicNetworkTransactionTest : public PlatformTest {
std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
QuicSpdyCompressor compressor;
if (QuicVersionMax() >= QUIC_VERSION_9) {
- return compressor.CompressHeadersWithPriority(0, headers);
+ return compressor.CompressHeadersWithPriority(
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY), headers);
}
return compressor.CompressHeaders(headers);
}
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index e1d0e21e19..609ebcbd73 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -12,6 +12,7 @@
using base::StringPiece;
using std::make_pair;
+using std::max;
using std::min;
using std::pair;
using std::vector;
@@ -31,7 +32,7 @@ QuicPacketCreator::QuicPacketCreator(QuicGuid guid,
send_version_in_packet_(!is_server),
sequence_number_length_(options_.send_sequence_number_length),
packet_size_(0) {
- framer_->set_fec_builder(reinterpret_cast<QuicFecBuilderInterface*>(this));
+ framer_->set_fec_builder(this);
}
QuicPacketCreator::~QuicPacketCreator() {
@@ -71,6 +72,30 @@ void QuicPacketCreator::StopSendingVersion() {
}
}
+void QuicPacketCreator::UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount bytes_per_second) {
+ DCHECK_LE(least_packet_awaited_by_peer, sequence_number_ + 1);
+ // Since the packet creator will not change sequence number length mid FEC
+ // group, include the size of an FEC group to be safe.
+ const QuicPacketSequenceNumber current_delta =
+ options_.max_packets_per_fec_group + sequence_number_ + 1
+ - least_packet_awaited_by_peer;
+ const uint64 congestion_window =
+ bytes_per_second / options_.max_packet_length;
+ const uint64 delta = max(current_delta, congestion_window);
+
+ if (delta < 1 << ((PACKET_1BYTE_SEQUENCE_NUMBER * 8) - 2)) {
+ options_.send_sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ } else if (delta < 1 << ((PACKET_2BYTE_SEQUENCE_NUMBER * 8) - 2)) {
+ options_.send_sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER;
+ } else if (delta < 1 << ((PACKET_4BYTE_SEQUENCE_NUMBER * 8) - 2)) {
+ options_.send_sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER;
+ } else {
+ options_.send_sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER;
+ }
+}
+
bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
QuicStreamOffset offset) const {
return BytesFree() >
@@ -99,7 +124,12 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
StreamFramePacketOverhead(
framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP));
- DCHECK(HasRoomForStreamFrame(id, offset));
+ if (!HasRoomForStreamFrame(id, offset)) {
+ LOG(DFATAL) << "No room for Stream frame, BytesFree: " << BytesFree()
+ << " MinStreamFrameSize: "
+ << QuicFramer::GetMinStreamFrameSize(
+ framer_->version(), id, offset, true);
+ }
const size_t free_bytes = BytesFree();
size_t bytes_consumed = 0;
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
index 0e0a8c7789..68b62166fe 100644
--- a/net/quic/quic_packet_creator.h
+++ b/net/quic/quic_packet_creator.h
@@ -70,6 +70,12 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
// Makes the framer not serialize the protocol version in sent packets.
void StopSendingVersion();
+ // Update the sequence number length to use in future packets as soon as it
+ // can be safely changed.
+ void UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount bytes_per_second);
+
// The overhead the framing will add for a packet with one frame.
static size_t StreamFramePacketOverhead(
QuicVersion version,
@@ -152,6 +158,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
QuicEncryptedPacket* SerializeVersionNegotiationPacket(
const QuicVersionVector& supported_versions);
+ // Sequence number of the last created packet, or 0 if no packets have been
+ // created.
QuicPacketSequenceNumber sequence_number() const {
return sequence_number_;
}
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc
index 193bb8895f..51133b6880 100644
--- a/net/quic/quic_packet_creator_test.cc
+++ b/net/quic/quic_packet_creator_test.cc
@@ -333,6 +333,53 @@ TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
client_framer_.ProcessPacket(*encrypted.get());
}
+TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.set_sequence_number(64);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.set_sequence_number(64 * 256);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.set_sequence_number(64 * 256 * 256);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.set_sequence_number(GG_UINT64_C(64) * 256 * 256 * 256 * 256);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+}
+
+TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.UpdateSequenceNumberLength(1, 10000);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.UpdateSequenceNumberLength(1, 10000 * 256);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.UpdateSequenceNumberLength(1, 10000 * 256 * 256);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+
+ creator_.UpdateSequenceNumberLength(
+ 1, GG_UINT64_C(1000) * 256 * 256 * 256 * 256);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator_.options()->send_sequence_number_length);
+}
+
INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization,
QuicPacketCreatorTest,
::testing::Values(false, true));
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
index 41161eb215..9151cb883b 100644
--- a/net/quic/quic_packet_generator.cc
+++ b/net/quic/quic_packet_generator.cc
@@ -70,13 +70,6 @@ void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
StringPiece data,
QuicStreamOffset offset,
- bool fin) {
- return ConsumeData(id, data, offset, fin, NULL);
-}
-
-QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
bool fin,
QuicAckNotifier* notifier) {
IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
@@ -88,6 +81,9 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
size_t total_bytes_consumed = 0;
bool fin_consumed = false;
+ if (!packet_creator_->HasRoomForStreamFrame(id, offset)) {
+ SerializeAndSendPacket();
+ }
while (delegate_->CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
handshake)) {
QuicFrame frame;
@@ -100,8 +96,13 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
bytes_consumed = packet_creator_->CreateStreamFrame(
id, data, offset + total_bytes_consumed, fin, &frame);
}
- bool success = AddFrame(frame);
- DCHECK(success);
+ if (!AddFrame(frame)) {
+ LOG(DFATAL) << "Failed to add stream frame.";
+ // Inability to add a STREAM frame creates an unrecoverable hole in a
+ // the stream, so it's best to close the connection.
+ delegate_->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return QuicConsumedData(0, false);
+ }
total_bytes_consumed += bytes_consumed;
fin_consumed = fin && bytes_consumed == data.size();
@@ -195,7 +196,7 @@ bool QuicPacketGenerator::HasPendingFrames() const {
bool QuicPacketGenerator::AddNextPendingFrame() {
if (should_send_ack_) {
- pending_ack_frame_.reset((delegate_->CreateAckFrame()));
+ pending_ack_frame_.reset(delegate_->CreateAckFrame());
// If we can't this add the frame now, then we still need to do so later.
should_send_ack_ = !AddFrame(QuicFrame(pending_ack_frame_.get()));
// Return success if we have cleared out this flag (i.e., added the frame).
@@ -204,7 +205,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() {
}
if (should_send_feedback_) {
- pending_feedback_frame_.reset((delegate_->CreateFeedbackFrame()));
+ pending_feedback_frame_.reset(delegate_->CreateFeedbackFrame());
// If we can't this add the frame now, then we still need to do so later.
should_send_feedback_ = !AddFrame(QuicFrame(pending_feedback_frame_.get()));
// Return success if we have cleared out this flag (i.e., added the frame).
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h
index 75472a17f5..940b259adf 100644
--- a/net/quic/quic_packet_generator.h
+++ b/net/quic/quic_packet_generator.h
@@ -15,7 +15,7 @@
// If the Delegate is not writable, then no operations will cause
// a packet to be serialized. In particular:
// * SetShouldSendAck will simply record that an ack is to be sent.
-// * AddControlFram will enqueue the control frame.
+// * AddControlFrame will enqueue the control frame.
// * ConsumeData will do nothing.
//
// If the Delegate is writable, then the behavior depends on the second
@@ -71,6 +71,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0;
// Takes ownership of |packet.packet| and |packet.retransmittable_frames|.
virtual bool OnSerializedPacket(const SerializedPacket& packet) = 0;
+ virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0;
};
// Interface which gets callbacks from the QuicPacketGenerator at interesting
@@ -90,20 +91,20 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator {
virtual ~QuicPacketGenerator();
+ // Indicates that an ACK frame should be sent. If |also_send_feedback| is
+ // true, then it also indicates a CONGESTION_FEEDBACK frame should be sent.
+ // The contents of the frame(s) will be generated via a call to the delegates
+ // CreateAckFrame() and CreateFeedbackFrame() when the packet is serialized.
void SetShouldSendAck(bool also_send_feedback);
void AddControlFrame(const QuicFrame& frame);
- // Given some data, may consume part or all of it and pass it to the packet
- // creator to be serialized into packets. If not in batch mode, these packets
- // will also be sent during this call.
- QuicConsumedData ConsumeData(QuicStreamId id,
- base::StringPiece data,
- QuicStreamOffset offset,
- bool fin);
-
- // As above, but attaches a QuicAckNotifier to any created stream frames,
- // which will be called once the frame is ACKed by the peer.
- // The QuicAckNotifier is owned by the QuicConnection.
+ // Given some data, may consume part or all of it and pass it to the
+ // packet creator to be serialized into packets. If not in batch
+ // mode, these packets will also be sent during this call. Also
+ // attaches a QuicAckNotifier to any created stream frames, which
+ // will be called once the frame is ACKed by the peer. The
+ // QuicAckNotifier is owned by the QuicConnection. |notifier| may
+ // be NULL.
QuicConsumedData ConsumeData(QuicStreamId id,
base::StringPiece data,
QuicStreamOffset offset,
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
index 88320426d2..ec8dd56a67 100644
--- a/net/quic/quic_packet_generator_test.cc
+++ b/net/quic/quic_packet_generator_test.cc
@@ -40,6 +40,7 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface {
MOCK_METHOD0(CreateAckFrame, QuicAckFrame*());
MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*());
MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet));
+ MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool));
void SetCanWriteAnything() {
EXPECT_CALL(*this, CanWrite(NOT_RETRANSMISSION, _, _))
@@ -326,7 +327,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
delegate_.SetCanNotWrite();
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -336,7 +337,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -347,7 +348,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
DoAll(SaveArg<0>(&packet_), Return(true)));
- QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true);
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true, NULL);
EXPECT_EQ(3u, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -362,8 +363,8 @@ TEST_F(QuicPacketGeneratorTest,
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, "foo", 2, true);
- QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(1, "foo", 2, true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -373,8 +374,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
delegate_.SetCanWriteAnything();
generator_.StartBatchOperations();
- generator_.ConsumeData(1, "foo", 2, true);
- QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(1, "foo", 2, true, NULL);
+ QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false, NULL);
EXPECT_EQ(4u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -413,7 +414,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
// Send enough data to create 3 packets: two full and one partial.
size_t data_len = 2 * kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -445,7 +446,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
// Send enough data to create 2 packets: one full and one partial.
size_t data_len = 1 * kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -455,6 +456,49 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
CheckPacketIsFec(packet3_, 1);
}
+TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
+ // Set the packet size be enough for two stream frames with 0 stream offset,
+ // but not enough for a stream frame of 0 offset and one with non-zero offset.
+ creator_.options()->max_packet_length =
+ NullEncrypter().GetCiphertextSize(0) +
+ GetPacketHeaderSize(creator_.options()->send_guid_length,
+ true,
+ creator_.options()->send_sequence_number_length,
+ NOT_IN_FEC_GROUP) +
+ // Add an extra 3 bytes for the payload and 1 byte so BytesFree is larger
+ // than the GetMinStreamFrameSize.
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false) + 3 +
+ QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true) + 1;
+ delegate_.SetCanWriteAnything();
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet_), Return(true)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ DoAll(SaveArg<0>(&packet2_), Return(true)));
+ }
+ generator_.StartBatchOperations();
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 0, false, NULL);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ // This frame will not fit with the existing frame, causing the queued frame
+ // to be serialized, and it will not fit with another frame like it, so it is
+ // serialized by itself.
+ consumed = generator_.ConsumeData(1, "bar", 3, true, NULL);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+ CheckPacketContains(contents, packet2_);
+}
+
TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
delegate_.SetCanNotWrite();
@@ -473,7 +517,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
Return(CreateFeedbackFrame()));
// Send some data and a control frame
- generator_.ConsumeData(3, "quux", 7, false);
+ generator_.ConsumeData(3, "quux", 7, false, NULL);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
// All five frames will be flushed out in a single packet.
@@ -520,7 +564,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
// Send enough data to exceed one packet
size_t data_len = kMaxPacketSize + 100;
QuicConsumedData consumed =
- generator_.ConsumeData(3, CreateData(data_len), 0, true);
+ generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL);
EXPECT_EQ(data_len, consumed.bytes_consumed);
EXPECT_TRUE(consumed.fin_consumed);
generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index cbd4cad639..cdf3c6ce4d 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -121,8 +121,6 @@ QuicVersion QuicVersionMin() {
QuicTag QuicVersionToQuicTag(const QuicVersion version) {
switch (version) {
- case QUIC_VERSION_7:
- return MakeQuicTag('Q', '0', '0', '7');
case QUIC_VERSION_8:
return MakeQuicTag('Q', '0', '0', '8');
case QUIC_VERSION_9:
@@ -138,14 +136,11 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) {
}
QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
- const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7');
const QuicTag quic_tag_v8 = MakeQuicTag('Q', '0', '0', '8');
const QuicTag quic_tag_v9 = MakeQuicTag('Q', '0', '0', '9');
const QuicTag quic_tag_v10 = MakeQuicTag('Q', '0', '1', '0');
- if (version_tag == quic_tag_v7) {
- return QUIC_VERSION_7;
- } else if (version_tag == quic_tag_v8) {
+ if (version_tag == quic_tag_v8) {
return QUIC_VERSION_8;
} else if (version_tag == quic_tag_v9) {
return QUIC_VERSION_9;
@@ -165,7 +160,6 @@ return #x
string QuicVersionToString(const QuicVersion version) {
switch (version) {
- RETURN_STRING_LITERAL(QUIC_VERSION_7);
RETURN_STRING_LITERAL(QUIC_VERSION_8);
RETURN_STRING_LITERAL(QUIC_VERSION_9);
RETURN_STRING_LITERAL(QUIC_VERSION_10);
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 75f6f58016..26e6c02768 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -189,7 +189,6 @@ enum QuicVersion {
// Special case to indicate unknown/unsupported QUIC version.
QUIC_VERSION_UNSUPPORTED = 0,
- QUIC_VERSION_7 = 7,
QUIC_VERSION_8 = 8,
QUIC_VERSION_9 = 9,
QUIC_VERSION_10 = 10, // Current version.
@@ -270,8 +269,8 @@ NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
enum QuicRstStreamErrorCode {
QUIC_STREAM_NO_ERROR = 0,
- // There was some server error which halted stream processing.
- QUIC_SERVER_ERROR_PROCESSING_STREAM,
+ // There was some error which halted stream processing.
+ QUIC_ERROR_PROCESSING_STREAM,
// We got two fin or reset offsets which did not match.
QUIC_MULTIPLE_TERMINATION_OFFSETS,
// We got bad payload and can not respond to it at the protocol level.
@@ -334,6 +333,8 @@ enum QuicErrorCode {
QUIC_PEER_GOING_AWAY = 16,
// A stream ID was invalid.
QUIC_INVALID_STREAM_ID = 17,
+ // A priority was invalid.
+ QUIC_INVALID_PRIORITY = 49,
// Too many streams already open.
QUIC_TOO_MANY_OPEN_STREAMS = 18,
// Received public reset for this connection.
@@ -356,6 +357,8 @@ enum QuicErrorCode {
QUIC_PACKET_WRITE_ERROR = 27,
// There was an error while reading from the socket.
QUIC_PACKET_READ_ERROR = 51,
+ // We received a STREAM_FRAME with no data and no fin flag set.
+ QUIC_INVALID_STREAM_FRAME = 50,
// Crypto errors.
diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc
index 4144aeeb9a..52ed6645c9 100644
--- a/net/quic/quic_protocol_test.cc
+++ b/net/quic/quic_protocol_test.cc
@@ -56,8 +56,8 @@ TEST(QuicProtocolTest, QuicVersionToQuicTag) {
#endif
// Explicitly test a specific version.
- EXPECT_EQ(MakeQuicTag('Q', '0', '0', '7'),
- QuicVersionToQuicTag(QUIC_VERSION_7));
+ EXPECT_EQ(MakeQuicTag('Q', '0', '1', '0'),
+ QuicVersionToQuicTag(QUIC_VERSION_10));
// Loop over all supported versions and make sure that we never hit the
// default case (i.e. all supported versions should be successfully converted
@@ -95,8 +95,8 @@ TEST(QuicProtocolTest, QuicTagToQuicVersion) {
#endif
// Explicitly test specific versions.
- EXPECT_EQ(QUIC_VERSION_7,
- QuicTagToQuicVersion(MakeQuicTag('Q', '0', '0', '7')));
+ EXPECT_EQ(QUIC_VERSION_10,
+ QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '0')));
for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
const QuicVersion& version = kSupportedQuicVersions[i];
@@ -127,17 +127,17 @@ TEST(QuicProtocolTest, QuicTagToQuicVersionUnsupported) {
}
TEST(QuicProtocolTest, QuicVersionToString) {
- EXPECT_EQ("QUIC_VERSION_7",
- QuicVersionToString(QUIC_VERSION_7));
+ EXPECT_EQ("QUIC_VERSION_8",
+ QuicVersionToString(QUIC_VERSION_8));
EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
- QuicVersion single_version[] = {QUIC_VERSION_7};
- EXPECT_EQ("QUIC_VERSION_7,", QuicVersionArrayToString(
+ QuicVersion single_version[] = {QUIC_VERSION_8};
+ EXPECT_EQ("QUIC_VERSION_8,", QuicVersionArrayToString(
single_version, arraysize(single_version)));
QuicVersion multiple_versions[] =
- {QUIC_VERSION_9, QUIC_VERSION_8, QUIC_VERSION_7};
- EXPECT_EQ("QUIC_VERSION_9,QUIC_VERSION_8,QUIC_VERSION_7,",
+ {QUIC_VERSION_10, QUIC_VERSION_9, QUIC_VERSION_8};
+ EXPECT_EQ("QUIC_VERSION_10,QUIC_VERSION_9,QUIC_VERSION_8,",
QuicVersionArrayToString(multiple_versions,
arraysize(multiple_versions)));
}
diff --git a/net/quic/quic_reliable_client_stream.cc b/net/quic/quic_reliable_client_stream.cc
index 26e6815bb1..06b3178cda 100644
--- a/net/quic/quic_reliable_client_stream.cc
+++ b/net/quic/quic_reliable_client_stream.cc
@@ -7,6 +7,7 @@
#include "base/callback_helpers.h"
#include "net/base/net_errors.h"
#include "net/quic/quic_session.h"
+#include "net/spdy/write_blocked_list.h"
namespace net {
@@ -54,6 +55,13 @@ void QuicReliableClientStream::OnCanWrite() {
}
}
+QuicPriority QuicReliableClientStream::EffectivePriority() const {
+ if (delegate_ && delegate_->HasSendHeadersComplete()) {
+ return ReliableQuicStream::EffectivePriority();
+ }
+ return kHighestPriority;
+}
+
int QuicReliableClientStream::WriteStreamData(
base::StringPiece data,
bool fin,
diff --git a/net/quic/quic_reliable_client_stream.h b/net/quic/quic_reliable_client_stream.h
index c482ee67fd..bf3fc158d4 100644
--- a/net/quic/quic_reliable_client_stream.h
+++ b/net/quic/quic_reliable_client_stream.h
@@ -48,6 +48,9 @@ class NET_EXPORT_PRIVATE QuicReliableClientStream : public ReliableQuicStream {
// Called when the stream is closed because of an error.
virtual void OnError(int error) = 0;
+ // Returns true if sending of headers has completed.
+ virtual bool HasSendHeadersComplete() = 0;
+
protected:
virtual ~Delegate() {}
@@ -65,6 +68,11 @@ class NET_EXPORT_PRIVATE QuicReliableClientStream : public ReliableQuicStream {
virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
virtual void TerminateFromPeer(bool half_close) OVERRIDE;
virtual void OnCanWrite() OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ // While the server's set_priority shouldn't be called externally, the creator
+ // of client-side streams should be able to set the priority.
+ using ReliableQuicStream::set_priority;
int WriteStreamData(base::StringPiece data,
bool fin,
diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc
index 504159159c..aaebda27fe 100644
--- a/net/quic/quic_reliable_client_stream_test.cc
+++ b/net/quic/quic_reliable_client_stream_test.cc
@@ -28,6 +28,7 @@ class MockDelegate : public QuicReliableClientStream::Delegate {
MOCK_METHOD2(OnDataReceived, int(const char*, int));
MOCK_METHOD1(OnClose, void(QuicErrorCode));
MOCK_METHOD1(OnError, void(int));
+ MOCK_METHOD0(HasSendHeadersComplete, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockDelegate);
@@ -86,7 +87,7 @@ TEST_F(QuicReliableClientStreamTest, WriteStreamData) {
const size_t kDataLen = arraysize(kData1);
// All data written.
- EXPECT_CALL(session_, WriteData(stream_.id(), _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
TestCompletionCallback callback;
EXPECT_EQ(OK, stream_.WriteStreamData(base::StringPiece(kData1, kDataLen),
@@ -94,13 +95,14 @@ TEST_F(QuicReliableClientStreamTest, WriteStreamData) {
}
TEST_F(QuicReliableClientStreamTest, WriteStreamDataAsync) {
+ EXPECT_CALL(delegate_, HasSendHeadersComplete());
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
const char kData1[] = "hello world";
const size_t kDataLen = arraysize(kData1);
// No data written.
- EXPECT_CALL(session_, WriteData(stream_.id(), _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _)).WillOnce(
Return(QuicConsumedData(0, false)));
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING,
@@ -109,7 +111,7 @@ TEST_F(QuicReliableClientStreamTest, WriteStreamDataAsync) {
ASSERT_FALSE(callback.have_result());
// All data written.
- EXPECT_CALL(session_, WriteData(stream_.id(), _, _, _)).WillOnce(
+ EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
stream_.OnCanWrite();
ASSERT_TRUE(callback.have_result());
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
new file mode 100644
index 0000000000..a8960f07aa
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -0,0 +1,221 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_packet_manager.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+using std::make_pair;
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicSentPacketManager::HelperInterface::~HelperInterface() {
+}
+
+QuicSentPacketManager::QuicSentPacketManager(bool is_server,
+ HelperInterface* helper)
+ : is_server_(is_server),
+ helper_(helper) {
+}
+
+QuicSentPacketManager::~QuicSentPacketManager() {
+ STLDeleteValues(&unacked_packets_);
+}
+
+void QuicSentPacketManager::OnSerializedPacket(
+ const SerializedPacket& serialized_packet) {
+ if (serialized_packet.packet->is_fec_packet()) {
+ unacked_fec_packets_.insert(make_pair(
+ serialized_packet.sequence_number,
+ serialized_packet.retransmittable_frames));
+ return;
+ }
+
+ if (serialized_packet.retransmittable_frames == NULL) {
+ // Don't track ack/congestion feedback packets.
+ return;
+ }
+
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first <
+ serialized_packet.sequence_number);
+ unacked_packets_[serialized_packet.sequence_number] =
+ serialized_packet.retransmittable_frames;
+ retransmission_map_[serialized_packet.sequence_number] =
+ RetransmissionInfo(serialized_packet.sequence_number,
+ serialized_packet.sequence_number_length);
+}
+
+void QuicSentPacketManager::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ DCHECK(ContainsKey(unacked_packets_, old_sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, old_sequence_number));
+ DCHECK(unacked_packets_.empty() ||
+ unacked_packets_.rbegin()->first < new_sequence_number);
+
+ RetransmissionInfo retransmission_info(
+ new_sequence_number, GetSequenceNumberLength(old_sequence_number));
+ retransmission_info.number_retransmissions =
+ retransmission_map_[old_sequence_number].number_retransmissions + 1;
+ retransmission_map_.erase(old_sequence_number);
+ retransmission_map_[new_sequence_number] = retransmission_info;
+
+ RetransmittableFrames* frames = unacked_packets_[old_sequence_number];
+ DCHECK(frames);
+ unacked_packets_.erase(old_sequence_number);
+ unacked_packets_[new_sequence_number] = frames;
+}
+
+void QuicSentPacketManager::HandleAckForSentPackets(
+ const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets) {
+ // Go through the packets we have not received an ack for and see if this
+ // incoming_ack shows they've been seen by the peer.
+ UnackedPacketMap::iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end()) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ if (sequence_number > helper_->GetPeerLargestObservedPacket()) {
+ // These are very new sequence_numbers.
+ break;
+ }
+ RetransmittableFrames* unacked = it->second;
+ if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
+ // Packet was acked, so remove it from our unacked packet list.
+ DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number;
+ acked_packets->insert(sequence_number);
+ delete unacked;
+ unacked_packets_.erase(it++);
+ retransmission_map_.erase(sequence_number);
+ } else {
+ // This is a packet which we planned on retransmitting and has not been
+ // seen at the time of this ack being sent out. See if it's our new
+ // lowest unacked packet.
+ DVLOG(1) << ENDPOINT << "still missing packet " << sequence_number;
+ ++it;
+ // The peer got packets after this sequence number. This is an explicit
+ // nack.
+ RetransmissionMap::iterator retransmission_it =
+ retransmission_map_.find(sequence_number);
+ if (retransmission_it == retransmission_map_.end()) {
+ continue;
+ }
+ size_t nack_count = ++(retransmission_it->second.number_nacks);
+ helper_->OnPacketNacked(sequence_number, nack_count);
+ }
+ }
+}
+
+void QuicSentPacketManager::HandleAckForSentFecPackets(
+ const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets) {
+ UnackedPacketMap::iterator it = unacked_fec_packets_.begin();
+ while (it != unacked_fec_packets_.end()) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ if (sequence_number > helper_->GetPeerLargestObservedPacket()) {
+ break;
+ }
+ if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Got an ack for fec packet: " << sequence_number;
+ acked_packets->insert(sequence_number);
+ unacked_fec_packets_.erase(it++);
+ } else {
+ DVLOG(1) << ENDPOINT << "Still missing ack for fec packet: "
+ << sequence_number;
+ ++it;
+ }
+ }
+}
+
+void QuicSentPacketManager::DiscardPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ UnackedPacketMap::iterator unacked_it =
+ unacked_packets_.find(sequence_number);
+ if (unacked_it == unacked_packets_.end()) {
+ // Packet was not meant to be retransmitted.
+ DCHECK(!ContainsKey(retransmission_map_, sequence_number));
+ return;
+ }
+
+ // Delete the unacked packet.
+ delete unacked_it->second;
+ unacked_packets_.erase(unacked_it);
+ retransmission_map_.erase(sequence_number);
+}
+
+bool QuicSentPacketManager::IsRetransmission(
+ QuicPacketSequenceNumber sequence_number) const {
+ RetransmissionMap::const_iterator it =
+ retransmission_map_.find(sequence_number);
+ return it != retransmission_map_.end() &&
+ it->second.number_retransmissions > 0;
+}
+
+size_t QuicSentPacketManager::GetRetransmissionCount(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+ RetransmissionMap::const_iterator it =
+ retransmission_map_.find(sequence_number);
+ return it->second.number_retransmissions;
+}
+
+bool QuicSentPacketManager::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_packets_, sequence_number);
+}
+
+bool QuicSentPacketManager::IsFecUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return ContainsKey(unacked_fec_packets_, sequence_number);
+}
+
+const RetransmittableFrames& QuicSentPacketManager::GetRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(unacked_packets_, sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+
+ return *unacked_packets_.find(sequence_number)->second;
+}
+
+QuicSequenceNumberLength QuicSentPacketManager::GetSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK(ContainsKey(unacked_packets_, sequence_number));
+ DCHECK(ContainsKey(retransmission_map_, sequence_number));
+
+ return retransmission_map_.find(
+ sequence_number)->second.sequence_number_length;
+}
+
+bool QuicSentPacketManager::HasUnackedPackets() const {
+ return !unacked_packets_.empty();
+}
+
+size_t QuicSentPacketManager::GetNumUnackedPackets() const {
+ return unacked_packets_.size();
+}
+
+QuicPacketSequenceNumber
+QuicSentPacketManager::GetLeastUnackedSentPacket() const {
+ if (unacked_packets_.empty()) {
+ // If there are no unacked packets, set the least unacked packet to
+ // the sequence number of the next packet sent.
+ return helper_->GetNextPacketSequenceNumber();
+ }
+
+ return unacked_packets_.begin()->first;
+}
+
+SequenceNumberSet QuicSentPacketManager::GetUnackedPackets() const {
+ SequenceNumberSet unacked_packets;
+ for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ unacked_packets.insert(it->first);
+ }
+ return unacked_packets;
+}
+
+} // namespace net
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
new file mode 100644
index 0000000000..355ea498b6
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.h
@@ -0,0 +1,140 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+#define NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+
+#include <deque>
+#include <list>
+#include <map>
+#include <queue>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicSentPacketManager {
+ public:
+ class NET_EXPORT_PRIVATE HelperInterface {
+ public:
+ virtual QuicPacketSequenceNumber GetPeerLargestObservedPacket() = 0;
+ virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() = 0;
+
+ // Called when a packet has been explicitly NACKd
+ virtual void OnPacketNacked(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count) = 0;
+ virtual ~HelperInterface();
+ };
+
+ QuicSentPacketManager(bool is_server, HelperInterface* helper);
+ virtual ~QuicSentPacketManager();
+
+ // Called when a new packet is serialized. If the packet contains
+ // retransmittable data, it will be added to the unacked packet map.
+ void OnSerializedPacket(const SerializedPacket& serialized_packet);
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // Replaces the old entry in the unacked packet map with the new
+ // sequence number.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number);
+
+ // Process the incoming ack looking for newly ack'd data packets.
+ void HandleAckForSentPackets(const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets);
+
+ // Process the incoming ack looking for newly ack'd FEC packets.
+ void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack,
+ SequenceNumberSet* acked_packets);
+
+ // Discards all information about packet |sequence_number|.
+ void DiscardPacket(QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if |sequence_number| is a retransmission of a packet.
+ bool IsRetransmission(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the number of times the data in the packet |sequence_number|
+ // has been transmitted.
+ size_t GetRetransmissionCount(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if the non-FEC packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if the FEC packet |sequence_number| is unacked.
+ bool IsFecUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the RetransmittableFrames for |sequence_number|.
+ const RetransmittableFrames& GetRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the length of the serialized sequence number for
+ // the packet |sequence_number|.
+ QuicSequenceNumberLength GetSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are any unacked packets.
+ bool HasUnackedPackets() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPackets() const;
+
+ // Returns the smallest sequence number of a sent packet which has not
+ // been acked by the peer. If all packets have been acked, returns the
+ // sequence number of the next packet that will be sent.
+ QuicPacketSequenceNumber GetLeastUnackedSentPacket() const;
+
+ // Returns the set of unacked packet sequence numbers.
+ SequenceNumberSet GetUnackedPackets() const;
+
+ private:
+ struct RetransmissionInfo {
+ RetransmissionInfo() {}
+ explicit RetransmissionInfo(QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length)
+ : sequence_number(sequence_number),
+ sequence_number_length(sequence_number_length),
+ number_nacks(0),
+ number_retransmissions(0) {
+ }
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicSequenceNumberLength sequence_number_length;
+ size_t number_nacks;
+ size_t number_retransmissions;
+ };
+
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ RetransmittableFrames*> UnackedPacketMap;
+ typedef base::hash_map<QuicPacketSequenceNumber,
+ RetransmissionInfo> RetransmissionMap;
+
+ // When new packets are created which may be retransmitted, they are added
+ // to this map, which contains owning pointers to the contained frames.
+ UnackedPacketMap unacked_packets_;
+
+ // Pending fec packets that have not been acked yet. These packets need to be
+ // cleared out of the cgst_window after a timeout since FEC packets are never
+ // retransmitted.
+ // TODO(satyamshekhar): What should be the timeout for these packets?
+ UnackedPacketMap unacked_fec_packets_;
+
+ // Map from sequence number to the retransmission info.
+ RetransmissionMap retransmission_map_;
+
+ // Tracks if the connection was created by the server.
+ bool is_server_;
+ HelperInterface* helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
diff --git a/net/quic/quic_sent_packet_manager_test.cc b/net/quic/quic_sent_packet_manager_test.cc
new file mode 100644
index 0000000000..8f5cb3d19d
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_packet_manager.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockHelper : public QuicSentPacketManager::HelperInterface {
+ public:
+ MOCK_METHOD0(GetPeerLargestObservedPacket, QuicPacketSequenceNumber());
+ MOCK_METHOD0(GetNextPacketSequenceNumber, QuicPacketSequenceNumber());
+ MOCK_METHOD2(OnPacketNacked, void(QuicPacketSequenceNumber sequence_number,
+ size_t nack_count));
+};
+
+class QuicSentPacketManagerTest : public ::testing::Test {
+ protected:
+ QuicSentPacketManagerTest()
+ : manager_(true, &helper_) {
+ }
+
+ testing::StrictMock<MockHelper> helper_;
+ QuicSentPacketManager manager_;
+};
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacket) {
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(1u));
+ EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnacked) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewDataPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u,
+ new RetransmittableFrames());
+
+ manager_.OnSerializedPacket(serialized_packet);
+ EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnackedFec) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewFecPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u, NULL);
+
+ manager_.OnSerializedPacket(serialized_packet);
+ // FEC packets do not count as "unacked".
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
+ EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketDiscardUnacked) {
+ scoped_ptr<QuicPacket> packet(QuicPacket::NewDataPacket(
+ NULL, 0, false, PACKET_8BYTE_GUID, false, PACKET_6BYTE_SEQUENCE_NUMBER));
+ SerializedPacket serialized_packet(1u, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet.get(), 7u,
+ new RetransmittableFrames());
+
+ manager_.OnSerializedPacket(serialized_packet);
+ manager_.DiscardPacket(1u);
+ EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u));
+ EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 5b7dc388f7..9389af403c 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -33,12 +33,8 @@ class VisitorShim : public QuicConnectionVisitorInterface {
public:
explicit VisitorShim(QuicSession* session) : session_(session) {}
- virtual bool OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const vector<QuicStreamFrame>& frame) OVERRIDE {
- bool accepted = session_->OnPacket(self_address, peer_address, header,
- frame);
+ virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
+ bool accepted = session_->OnStreamFrames(frames);
session_->PostProcessAfterData();
return accepted;
}
@@ -52,22 +48,26 @@ class VisitorShim : public QuicConnectionVisitorInterface {
session_->PostProcessAfterData();
}
- virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {
- session_->OnAck(acked_packets);
- session_->PostProcessAfterData();
- }
-
virtual bool OnCanWrite() OVERRIDE {
bool rc = session_->OnCanWrite();
session_->PostProcessAfterData();
return rc;
}
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE {
+ session_->OnSuccessfulVersionNegotiation(version);
+ }
+
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE {
session_->ConnectionClose(error, from_peer);
// The session will go away, so don't bother with cleanup.
}
+ virtual bool HasPendingHandshake() const OVERRIDE {
+ return session_->HasPendingHandshake();
+ }
+
private:
QuicSession* session_;
};
@@ -84,7 +84,8 @@ QuicSession::QuicSession(QuicConnection* connection,
largest_peer_created_stream_id_(0),
error_(QUIC_NO_ERROR),
goaway_received_(false),
- goaway_sent_(false) {
+ goaway_sent_(false),
+ has_pending_handshake_(false) {
connection_->set_visitor(visitor_shim_.get());
connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime());
@@ -100,16 +101,7 @@ QuicSession::~QuicSession() {
STLDeleteValues(&stream_map_);
}
-bool QuicSession::OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const vector<QuicStreamFrame>& frames) {
- if (header.public_header.guid != connection()->guid()) {
- DLOG(INFO) << ENDPOINT << "Got packet header for invalid GUID: "
- << header.public_header.guid;
- return false;
- }
-
+bool QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
for (size_t i = 0; i < frames.size(); ++i) {
// TODO(rch) deal with the error case of stream id 0
if (IsClosedStream(frames[i].stream_id)) {
@@ -217,11 +209,17 @@ bool QuicSession::OnCanWrite() {
while (!connection_->HasQueuedData() &&
remaining_writes > 0) {
DCHECK(write_blocked_streams_.HasWriteBlockedStreams());
- ReliableQuicStream* stream = NULL;
int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList();
- if (index != -1) {
- stream = GetStream(write_blocked_streams_.PopFront(index));
+ if (index == -1) {
+ LOG(DFATAL) << "WriteBlockedStream is missing";
+ connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return true; // We have no write blocked streams.
}
+ QuicStreamId stream_id = write_blocked_streams_.PopFront(index);
+ if (stream_id == kCryptoStreamId) {
+ has_pending_handshake_ = false; // We just popped it.
+ }
+ ReliableQuicStream* stream = GetStream(stream_id);
if (stream != NULL) {
// If the stream can't write all bytes, it'll re-add itself to the blocked
// list.
@@ -233,11 +231,16 @@ bool QuicSession::OnCanWrite() {
return !write_blocked_streams_.HasWriteBlockedStreams();
}
-QuicConsumedData QuicSession::WriteData(QuicStreamId id,
- StringPiece data,
- QuicStreamOffset offset,
- bool fin) {
- return connection_->SendStreamData(id, data, offset, fin);
+bool QuicSession::HasPendingHandshake() const {
+ return has_pending_handshake_;
+}
+
+QuicConsumedData QuicSession::WritevData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin) {
+ return connection_->SendvStreamData(id, iov, iov_count, offset, fin);
}
void QuicSession::SendRstStream(QuicStreamId id,
@@ -375,7 +378,7 @@ QuicConfig* QuicSession::config() {
void QuicSession::ActivateStream(ReliableQuicStream* stream) {
DLOG(INFO) << ENDPOINT << "num_streams: " << stream_map_.size()
<< ". activating " << stream->id();
- DCHECK(stream_map_.count(stream->id()) == 0);
+ DCHECK_EQ(stream_map_.count(stream->id()), 0u);
stream_map_[stream->id()] = stream;
}
@@ -474,8 +477,16 @@ size_t QuicSession::GetNumOpenStreams() const {
zombie_streams_.size();
}
-void QuicSession::MarkWriteBlocked(QuicStreamId id) {
- write_blocked_streams_.PushBack(id, 0);
+void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
+ if (id == kCryptoStreamId) {
+ DCHECK(!has_pending_handshake_);
+ has_pending_handshake_ = true;
+ // TODO(jar): Be sure to use the highest priority for the crypto stream,
+ // perhaps by adding a "special" priority for it that is higher than
+ // kHighestPriority.
+ priority = kHighestPriority;
+ }
+ write_blocked_streams_.PushBack(id, priority);
}
void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id,
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index c37b6e11c3..b58feb2742 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -59,26 +59,28 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
virtual ~QuicSession();
// QuicConnectionVisitorInterface methods:
- virtual bool OnPacket(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame) OVERRIDE;
+ virtual bool OnStreamFrames(
+ const std::vector<QuicStreamFrame>& frames) OVERRIDE;
virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE{}
// Not needed for HTTP.
- virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {}
virtual bool OnCanWrite() OVERRIDE;
+ virtual bool HasPendingHandshake() const OVERRIDE;
// Called by streams when they want to write data to the peer.
// Returns a pair with the number of bytes consumed from data, and a boolean
// indicating if the fin bit was consumed. This does not indicate the data
// has been sent on the wire: it may have been turned into a packet and queued
// if the socket was unexpectedly blocked.
- virtual QuicConsumedData WriteData(QuicStreamId id,
- base::StringPiece data,
- QuicStreamOffset offset,
- bool fin);
+ virtual QuicConsumedData WritevData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin);
+
// Called by streams when they want to close the stream in both directions.
virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error);
@@ -137,7 +139,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// been implicitly created.
virtual size_t GetNumOpenStreams() const;
- void MarkWriteBlocked(QuicStreamId id);
+ void MarkWriteBlocked(QuicStreamId id, QuicPriority priority);
// Marks that |stream_id| is blocked waiting to decompress the
// headers identified by |decompression_id|.
@@ -282,6 +284,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
// Whether a GoAway has been sent.
bool goaway_sent_;
+ // Indicate if there is pending data for the crypto stream.
+ bool has_pending_handshake_;
+
DISALLOW_COPY_AND_ASSIGN(QuicSession);
};
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index b7953aee11..e90b20f81c 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -23,12 +23,15 @@ using std::set;
using std::vector;
using testing::_;
using testing::InSequence;
+using testing::InvokeWithoutArgs;
using testing::StrictMock;
namespace net {
namespace test {
namespace {
+const QuicPriority kSomeMiddlePriority = 2;
+
class TestCryptoStream : public QuicCryptoStream {
public:
explicit TestCryptoStream(QuicSession* session)
@@ -47,6 +50,8 @@ class TestCryptoStream : public QuicCryptoStream {
EXPECT_EQ(QUIC_NO_ERROR, error);
session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
}
+
+ MOCK_METHOD0(OnCanWrite, void());
};
class TestStream : public ReliableQuicStream {
@@ -64,6 +69,23 @@ class TestStream : public ReliableQuicStream {
MOCK_METHOD0(OnCanWrite, void());
};
+// Poor man's functor for use as callback in a mock.
+class StreamBlocker {
+ public:
+ StreamBlocker(QuicSession* session, QuicStreamId stream_id)
+ : session_(session),
+ stream_id_(stream_id) {
+ }
+
+ void MarkWriteBlocked() {
+ session_->MarkWriteBlocked(stream_id_, kSomeMiddlePriority);
+ }
+
+ private:
+ QuicSession* const session_;
+ const QuicStreamId stream_id_;
+};
+
class TestSession : public QuicSession {
public:
TestSession(QuicConnection* connection, bool is_server)
@@ -71,7 +93,7 @@ class TestSession : public QuicSession {
crypto_stream_(this) {
}
- virtual QuicCryptoStream* GetCryptoStream() OVERRIDE {
+ virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
return &crypto_stream_;
}
@@ -93,11 +115,6 @@ class TestSession : public QuicSession {
return QuicSession::GetIncomingReliableStream(stream_id);
}
- // Helper method for gmock
- void MarkTwoWriteBlocked() {
- this->MarkWriteBlocked(2);
- }
-
TestCryptoStream crypto_stream_;
};
@@ -240,29 +257,77 @@ TEST_F(QuicSessionTest, OnCanWrite) {
TestStream* stream4 = session_.CreateOutgoingReliableStream();
TestStream* stream6 = session_.CreateOutgoingReliableStream();
- session_.MarkWriteBlocked(2);
- session_.MarkWriteBlocked(6);
- session_.MarkWriteBlocked(4);
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
InSequence s;
+ StreamBlocker stream2_blocker(&session_, stream2->id());
EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
// Reregister, to test the loop limit.
- testing::InvokeWithoutArgs(&session_, &TestSession::MarkTwoWriteBlocked));
+ InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
EXPECT_CALL(*stream6, OnCanWrite());
EXPECT_CALL(*stream4, OnCanWrite());
EXPECT_FALSE(session_.OnCanWrite());
}
+TEST_F(QuicSessionTest, BufferedHandshake) {
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
+
+ // Test that blocking other streams does not change our status.
+ TestStream* stream2 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ stream2_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ TestStream* stream3 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream3_blocker(&session_, stream3->id());
+ stream3_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ // Blocking (due to buffering of) the Crypto stream is detected.
+ session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority);
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ TestStream* stream4 = session_.CreateOutgoingReliableStream();
+ StreamBlocker stream4_blocker(&session_, stream4->id());
+ stream4_blocker.MarkWriteBlocked();
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ InSequence s;
+ // Force most streams to re-register, which is common scenario when we block
+ // the Crypto stream, and only the crypto stream can "really" write.
+
+ // Due to prioritization, we *should* be asked to write the crypto stream
+ // first.
+ // Don't re-register the crypto stream (which signals complete writing).
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ // Re-register all other streams, to show they weren't able to proceed.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(
+ InvokeWithoutArgs(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ EXPECT_FALSE(session_.OnCanWrite());
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
+}
+
TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) {
TestStream* stream2 = session_.CreateOutgoingReliableStream();
TestStream* stream4 = session_.CreateOutgoingReliableStream();
- session_.CreateOutgoingReliableStream(); // stream 6
+ TestStream* stream6 = session_.CreateOutgoingReliableStream();
- session_.MarkWriteBlocked(2);
- session_.MarkWriteBlocked(6);
- session_.MarkWriteBlocked(4);
- CloseStream(6);
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+ CloseStream(stream6->id());
InSequence s;
EXPECT_CALL(*stream2, OnCanWrite());
@@ -293,11 +358,11 @@ TEST_F(QuicSessionTest, OutOfOrderHeaders) {
// Process the second frame first. This will cause the headers to
// be queued up and processed after the first frame is processed.
frames.push_back(frame2);
- session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+ session_.OnStreamFrames(frames);
// Process the first frame, and un-cork the buffered headers.
frames[0] = frame1;
- session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+ session_.OnStreamFrames(frames);
// Ensure that the streams actually close and we don't DCHECK.
connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
@@ -356,7 +421,8 @@ TEST_F(QuicSessionTest, ZombieStream) {
// be queued up and processed after the first frame is processed.
frames.push_back(frame1);
EXPECT_FALSE(stream3->headers_decompressed());
- session.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+
+ session.OnStreamFrames(frames);
EXPECT_EQ(1u, session.GetNumOpenStreams());
EXPECT_TRUE(connection->connected());
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index e5319fb474..e4b4d99a9a 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -159,16 +159,6 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
if (rv != OK)
return rv;
- // TODO(rch): remove this code!
- AddressList::iterator it = address_list_.begin();
- while (it != address_list_.end()) {
- if (it->GetFamily() == ADDRESS_FAMILY_IPV6) {
- it = address_list_.erase(it);
- } else {
- it++;
- }
- }
-
DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
io_state_ = STATE_CONNECT;
return OK;
@@ -307,8 +297,7 @@ int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair,
void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
if (rv == OK) {
- // TODO(rch): Uncomment this once we trust 0-RTT
- // require_confirmation_ = false;
+ require_confirmation_ = false;
// Create all the streams, but do not notify them yet.
for (RequestSet::iterator it = job_requests_map_[job].begin();
@@ -437,18 +426,10 @@ QuicClientSession* QuicStreamFactory::CreateSession(
// revisit this setting and test for its impact.
const int32 kSocketBufferSize(kMaxPacketSize * 100); // Support 100 packets.
socket->SetReceiveBufferSize(kSocketBufferSize);
- // TODO(jar): What should the UDP send buffer be set to? If the send buffer
- // is too large, then we might(?) wastefully queue packets in the OS, when
- // we'd rather construct packets just in time. We do however expect that the
- // calculated send rate (paced, or ack clocked), will be well below the egress
- // rate of the local machine, so that *shouldn't* be a problem.
- // If the buffer setting is too small, then we will starve our outgoing link
- // on a fast connection, because we won't respond fast enough to the many
- // async callbacks to get data from us. On the other hand, until we have real
- // pacing support (beyond ack-clocked pacing), we get a bit of adhoc-pacing by
- // requiring the application to refill this OS buffer (ensuring that we don't
- // blast a pile of packets at the kernel's max egress rate).
- // socket->SetSendBufferSize(????);
+ // Set a buffer large enough to contain the initial CWND's worth of packet
+ // to work around the problem with CHLO packets being sent out with the
+ // wrong encryption level, when the send buffer is full.
+ socket->SetSendBufferSize(kMaxPacketSize * 20); // Support 20 packets.
QuicConnectionHelper* helper = new QuicConnectionHelper(
base::MessageLoop::current()->message_loop_proxy().get(),
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index fb9d474eb8..a546d874ea 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -52,7 +52,7 @@ class QuicStreamFactoryTest : public ::testing::Test {
header.fec_flag = false;
header.fec_group = 0;
- QuicRstStreamFrame rst(stream_id, QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ QuicRstStreamFrame rst(stream_id, QUIC_ERROR_PROCESSING_STREAM);
return scoped_ptr<QuicEncryptedPacket>(
ConstructPacket(header, QuicFrame(&rst)));
}
diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc
index 7cf67d351e..a57c05f83c 100644
--- a/net/quic/quic_stream_sequencer.cc
+++ b/net/quic/quic_stream_sequencer.cc
@@ -74,17 +74,21 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
return true;
}
- if (frame.fin) {
- CloseStreamAtOffset(frame.offset + frame.data.size());
- }
-
QuicStreamOffset byte_offset = frame.offset;
const char* data = frame.data.data();
size_t data_len = frame.data.size();
- if (data_len == 0) {
- // TODO(rch): Close the stream if there was no data and no fin.
- return true;
+ if (data_len == 0 && !frame.fin) {
+ // Stream frames must have data or a fin flag.
+ stream_->ConnectionClose(QUIC_INVALID_STREAM_FRAME, false);
+ return false;
+ }
+
+ if (frame.fin) {
+ CloseStreamAtOffset(frame.offset + frame.data.size());
+ if (data_len == 0) {
+ return true;
+ }
}
if (byte_offset == num_bytes_consumed_) {
@@ -96,7 +100,7 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
return true;
}
if (bytes_consumed > data_len) {
- stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ stream_->Close(QUIC_ERROR_PROCESSING_STREAM);
return false;
} else if (bytes_consumed == data_len) {
FlushBufferedFrames();
@@ -211,7 +215,7 @@ void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) {
<< " end_offset: " << end_offset
<< " offset: " << it->first
<< " length: " << it->second.length();
- stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ stream_->Close(QUIC_ERROR_PROCESSING_STREAM);
return;
}
@@ -262,7 +266,7 @@ void QuicStreamSequencer::FlushBufferedFrames() {
return;
}
if (bytes_consumed > data->size()) {
- stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM); // Programming error
+ stream_->Close(QUIC_ERROR_PROCESSING_STREAM); // Programming error
return;
} else if (bytes_consumed == data->size()) {
frames_.erase(it);
diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc
index 878585ba87..21568d6065 100644
--- a/net/quic/quic_stream_sequencer_test.cc
+++ b/net/quic/quic_stream_sequencer_test.cc
@@ -71,6 +71,7 @@ class MockStream : public ReliableQuicStream {
MOCK_METHOD1(TerminateFromPeer, void(bool half_close));
MOCK_METHOD2(ProcessData, uint32(const char* data, uint32 data_len));
+ MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer));
MOCK_METHOD1(Close, void(QuicRstStreamErrorCode error));
MOCK_METHOD0(OnCanWrite, void());
};
@@ -182,7 +183,8 @@ TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
}
TEST_F(QuicStreamSequencerTest, EmptyFrame) {
- EXPECT_TRUE(sequencer_->OnFrame(0, ""));
+ EXPECT_CALL(stream_, ConnectionClose(QUIC_INVALID_STREAM_FRAME, false));
+ EXPECT_FALSE(sequencer_->OnFrame(0, ""));
EXPECT_EQ(0u, sequencer_->frames()->size());
EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
}
@@ -399,7 +401,7 @@ TEST_F(QuicStreamSequencerTest, MarkConsumedError) {
// Now, attempt to mark consumed more data than was readable
// and expect the stream to be closed.
- EXPECT_CALL(stream_, Close(QUIC_SERVER_ERROR_PROCESSING_STREAM));
+ EXPECT_CALL(stream_, Close(QUIC_ERROR_PROCESSING_STREAM));
EXPECT_DFATAL(sequencer_->MarkConsumed(4),
"Invalid argument to MarkConsumed. num_bytes_consumed_: 3 "
"end_offset: 4 offset: 9 length: 17");
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index 74c90a0bc3..cfb3cb7db5 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -120,7 +120,7 @@ const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) {
switch (error) {
RETURN_STRING_LITERAL(QUIC_STREAM_NO_ERROR);
RETURN_STRING_LITERAL(QUIC_STREAM_CONNECTION_ERROR);
- RETURN_STRING_LITERAL(QUIC_SERVER_ERROR_PROCESSING_STREAM);
+ RETURN_STRING_LITERAL(QUIC_ERROR_PROCESSING_STREAM);
RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS);
RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
@@ -171,6 +171,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP);
RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND);
RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY);
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
@@ -182,6 +183,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS);
RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR);
RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_FRAME);
RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 45b4fce4ae..41cb4576b5 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -6,6 +6,7 @@
#include "net/quic/quic_session.h"
#include "net/quic/quic_spdy_decompressor.h"
+#include "net/spdy/write_blocked_list.h"
using base::StringPiece;
using std::min;
@@ -193,6 +194,12 @@ QuicConsumedData ReliableQuicStream::WriteData(StringPiece data, bool fin) {
return WriteOrBuffer(data, fin);
}
+
+void ReliableQuicStream::set_priority(QuicPriority priority) {
+ DCHECK_EQ(0u, stream_bytes_written_);
+ priority_ = priority;
+}
+
QuicConsumedData ReliableQuicStream::WriteOrBuffer(StringPiece data, bool fin) {
DCHECK(!fin_buffered_);
@@ -235,27 +242,43 @@ void ReliableQuicStream::OnCanWrite() {
QuicConsumedData ReliableQuicStream::WriteDataInternal(
StringPiece data, bool fin) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return WritevDataInternal(&iov, 1, fin);
+}
+
+QuicConsumedData ReliableQuicStream::WritevDataInternal(const struct iovec* iov,
+ int iov_count,
+ bool fin) {
if (write_side_closed_) {
DLOG(ERROR) << "Attempt to write when the write side is closed";
return QuicConsumedData(0, false);
}
+ size_t write_length = 0u;
+ for (int i = 0; i < iov_count; ++i) {
+ write_length += iov[i].iov_len;
+ }
QuicConsumedData consumed_data =
- session()->WriteData(id(), data, stream_bytes_written_, fin);
+ session()->WritevData(id(), iov, iov_count, stream_bytes_written_, fin);
stream_bytes_written_ += consumed_data.bytes_consumed;
- if (consumed_data.bytes_consumed == data.length()) {
+ if (consumed_data.bytes_consumed == write_length) {
if (fin && consumed_data.fin_consumed) {
fin_sent_ = true;
CloseWriteSide();
} else if (fin && !consumed_data.fin_consumed) {
- session_->MarkWriteBlocked(id());
+ session_->MarkWriteBlocked(id(), EffectivePriority());
}
} else {
- session_->MarkWriteBlocked(id());
+ session_->MarkWriteBlocked(id(), EffectivePriority());
}
return consumed_data;
}
+QuicPriority ReliableQuicStream::EffectivePriority() const {
+ return priority();
+}
+
void ReliableQuicStream::CloseReadSide() {
if (read_side_closed_) {
return;
@@ -270,10 +293,8 @@ void ReliableQuicStream::CloseReadSide() {
}
uint32 ReliableQuicStream::ProcessRawData(const char* data, uint32 data_len) {
+ DCHECK_NE(0u, data_len);
if (id() == kCryptoStreamId) {
- if (data_len == 0) {
- return 0;
- }
// The crypto stream does not use compression.
return ProcessData(data, data_len);
}
@@ -283,7 +304,7 @@ uint32 ReliableQuicStream::ProcessRawData(const char* data, uint32 data_len) {
total_bytes_consumed += StripPriorityAndHeaderId(data, data_len);
data += total_bytes_consumed;
data_len -= total_bytes_consumed;
- if (data_len == 0) {
+ if (data_len == 0 || !session_->connection()->connected()) {
return total_bytes_consumed;
}
}
@@ -465,11 +486,18 @@ uint32 ReliableQuicStream::StripPriorityAndHeaderId(
if (!priority_parsed_ &&
session_->connection()->version() >= QUIC_VERSION_9 &&
session_->connection()->is_server()) {
+ QuicPriority temporary_priority = priority_;
total_bytes_parsed = StripUint32(
- data, data_len, &headers_id_and_priority_buffer_, &priority_);
+ data, data_len, &headers_id_and_priority_buffer_, &temporary_priority);
if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) {
- // TODO(alyssar) check for priority out of bounds.
priority_parsed_ = true;
+ // Spdy priorities are inverted, so the highest numerical value is the
+ // lowest legal priority.
+ if (temporary_priority > static_cast<QuicPriority>(kLowestPriority)) {
+ session_->connection()->SendConnectionClose(QUIC_INVALID_PRIORITY);
+ return 0;
+ }
+ priority_ = temporary_priority;
}
data += total_bytes_parsed;
data_len -= total_bytes_parsed;
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 7ba5428412..807882ca2d 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -95,6 +95,12 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
// becomes unblocked.
virtual void OnDecompressorAvailable();
+ // By default, this is the same as priority(), however it allows streams
+ // to temporarily alter effective priority. For example if a SPDY stream has
+ // compressed but not written headers it can write the headers with a higher
+ // priority.
+ virtual QuicPriority EffectivePriority() const;
+
QuicStreamId id() const { return id_; }
QuicRstStreamErrorCode stream_error() const { return stream_error_; }
@@ -116,7 +122,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
bool GetSSLInfo(SSLInfo* ssl_info);
bool headers_decompressed() const { return headers_decompressed_; }
- QuicPriority priority() const { return priority_; }
protected:
// Returns a pair with the number of bytes consumed from data, and a boolean
@@ -141,6 +146,13 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
QuicSession* session() { return session_; }
+ // Sets priority_ to priority. This should only be called before bytes are
+ // written to the server.
+ void set_priority(QuicPriority priority);
+ // This is protected because external classes should use EffectivePriority
+ // instead.
+ QuicPriority priority() const { return priority_; }
+
// Sends as much of 'data' to the connection as the connection will consume,
// and then buffers any remaining data in queued_data_.
// Returns (data.size(), true) as it always consumed all data: it returns for
@@ -151,6 +163,13 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public
// Returns the number of bytes consumed by the connection.
QuicConsumedData WriteDataInternal(base::StringPiece data, bool fin);
+ // Sends as many bytes in the first |count| buffers of |iov| to the connection
+ // as the connection will consume.
+ // Returns the number of bytes consumed by the connection.
+ QuicConsumedData WritevDataInternal(const struct iovec* iov,
+ int iov_count,
+ bool fin);
+
private:
friend class test::ReliableQuicStreamPeer;
friend class QuicStreamUtils;
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index 1df13cfc14..063554d82c 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -126,9 +126,7 @@ TEST_F(ReliableQuicStreamTest, WriteAllData) {
1 + QuicPacketCreator::StreamFramePacketOverhead(
connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- // TODO(rch): figure out how to get StrEq working here.
- //EXPECT_CALL(*session_, WriteData(kStreamId, StrEq(kData1), _, _)).WillOnce(
- EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen, true)));
EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed);
EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
@@ -142,7 +140,7 @@ TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) {
// Write no data and no fin. If we consume nothing we should not be write
// blocked.
EXPECT_DEBUG_DEATH({
- EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(0, false)));
stream_->WriteData(StringPiece(), false);
EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams());
@@ -155,7 +153,7 @@ TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) {
// Write some data and no fin. If we consume some but not all of the data,
// we should be write blocked a not all the data was consumed.
- EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(1, false)));
stream_->WriteData(StringPiece(kData1, 2), false);
ASSERT_EQ(1, write_blocked_list_->NumBlockedStreams());
@@ -169,7 +167,7 @@ TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) {
// we should be write blocked because the fin was not consumed.
// (This should never actually happen as the fin should be sent out with the
// last data)
- EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(2, false)));
stream_->WriteData(StringPiece(kData1, 2), true);
ASSERT_EQ(1, write_blocked_list_->NumBlockedStreams());
@@ -180,7 +178,7 @@ TEST_F(ReliableQuicStreamTest, BlockIfSoloFinNotConsumed) {
// Write no data and a fin. If we consume nothing we should be write blocked,
// as the fin was not consumed.
- EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(0, false)));
stream_->WriteData(StringPiece(), true);
ASSERT_EQ(1, write_blocked_list_->NumBlockedStreams());
@@ -194,9 +192,7 @@ TEST_F(ReliableQuicStreamTest, WriteData) {
1 + QuicPacketCreator::StreamFramePacketOverhead(
connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion,
PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
- // TODO(rch): figure out how to get StrEq working here.
- //EXPECT_CALL(*session_, WriteData(_, StrEq(kData1), _, _)).WillOnce(
- EXPECT_CALL(*session_, WriteData(_, _, _, _)).WillOnce(
+ EXPECT_CALL(*session_, WritevData(_, _, 1, _, _)).WillOnce(
Return(QuicConsumedData(kDataLen - 1, false)));
// The return will be kDataLen, because the last byte gets buffered.
EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed);
@@ -207,17 +203,14 @@ TEST_F(ReliableQuicStreamTest, WriteData) {
// Make sure we get the tail of the first write followed by the bytes_consumed
InSequence s;
- //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData1[kDataLen - 1]), _, _)).
- EXPECT_CALL(*session_, WriteData(_, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, 1, _, _)).
WillOnce(Return(QuicConsumedData(1, false)));
- //EXPECT_CALL(*session_, WriteData(_, StrEq(kData2), _, _)).
- EXPECT_CALL(*session_, WriteData(_, _, _, _)).
+ EXPECT_CALL(*session_, WritevData(_, _, 1, _, _)).
WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
stream_->OnCanWrite();
- // And finally the end of the bytes_consumed
- //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData2[kDataLen - 2]), _, _)).
- EXPECT_CALL(*session_, WriteData(_, _, _, _)).
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, 1, _, _)).
WillOnce(Return(QuicConsumedData(2, true)));
stream_->OnCanWrite();
}
@@ -238,19 +231,20 @@ TEST_F(ReliableQuicStreamTest, ProcessHeaders) {
Initialize(kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame(kStreamId, false, 0, compressed_headers);
stream_->OnStreamFrame(frame);
EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data());
- EXPECT_EQ(0u, stream_->priority());
+ EXPECT_EQ(static_cast<QuicPriority>(kHighestPriority),
+ stream_->EffectivePriority());
}
TEST_F(ReliableQuicStreamTest, ProcessHeadersWithInvalidHeaderId) {
Initialize(kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
compressed_headers.replace(4, 1, 1, '\xFF'); // Illegal header id.
QuicStreamFrame frame(kStreamId, false, 0, compressed_headers);
@@ -262,7 +256,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBody) {
Initialize(kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
string body = "this is the body";
string data = compressed_headers + body;
QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -276,7 +270,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyFragments) {
Initialize(kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(7, headers_);
+ compressor_->CompressHeadersWithPriority(kLowestPriority, headers_);
string body = "this is the body";
string data = compressed_headers + body;
@@ -308,14 +302,15 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyFragments) {
ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
stream_->data()) << "split_point: " << split_point;
}
- EXPECT_EQ(7u, stream_->priority());
+ EXPECT_EQ(static_cast<QuicPriority>(kLowestPriority),
+ stream_->EffectivePriority());
}
TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyReadv) {
Initialize(!kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
string body = "this is the body";
string data = compressed_headers + body;
QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -345,7 +340,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
Initialize(!kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
string body = "this is the body";
string data = compressed_headers + body;
QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -371,7 +366,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
Initialize(!kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
string body = "this is the body";
string data = compressed_headers + body;
QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -401,14 +396,14 @@ TEST_F(ReliableQuicStreamTest, ProcessCorruptHeadersEarly) {
Initialize(kShouldProcessData);
string compressed_headers1 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
string decompressed_headers1 =
SpdyUtils::SerializeUncompressedHeaders(headers_);
headers_["content-type"] = "text/plain";
string compressed_headers2 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
// Corrupt the compressed data.
compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1;
QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2);
@@ -441,14 +436,14 @@ TEST_F(ReliableQuicStreamTest, ProcessPartialHeadersEarly) {
Initialize(kShouldProcessData);
string compressed_headers1 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
string decompressed_headers1 =
SpdyUtils::SerializeUncompressedHeaders(headers_);
headers_["content-type"] = "text/plain";
string compressed_headers2 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
string partial_compressed_headers =
compressed_headers2.substr(0, compressed_headers2.length() / 2);
QuicStreamFrame frame2(stream2_->id(), false, 0, partial_compressed_headers);
@@ -492,14 +487,14 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersEarly) {
Initialize(kShouldProcessData);
string compressed_headers1 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
string decompressed_headers1 =
SpdyUtils::SerializeUncompressedHeaders(headers_);
headers_["content-type"] = "text/plain";
string compressed_headers2 =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2);
string decompressed_headers2 =
SpdyUtils::SerializeUncompressedHeaders(headers_);
@@ -528,7 +523,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersDelay) {
Initialize(!kShouldProcessData);
string compressed_headers =
- compressor_->CompressHeadersWithPriority(0, headers_);
+ compressor_->CompressHeadersWithPriority(kHighestPriority, headers_);
QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers);
string decompressed_headers =
SpdyUtils::SerializeUncompressedHeaders(headers_);
diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc
index c168313202..5d715db4ef 100644
--- a/net/quic/test_tools/quic_connection_peer.cc
+++ b/net/quic/test_tools/quic_connection_peer.cc
@@ -70,17 +70,15 @@ QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout(
bool QuicConnectionPeer::IsSavedForRetransmission(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number) {
- return ContainsKey(connection->retransmission_map_, sequence_number);
+ return connection->sent_packet_manager_.IsUnacked(sequence_number);
}
// static
size_t QuicConnectionPeer::GetRetransmissionCount(
QuicConnection* connection,
QuicPacketSequenceNumber sequence_number) {
- QuicConnection::RetransmissionMap::iterator it =
- connection->retransmission_map_.find(sequence_number);
- DCHECK(connection->retransmission_map_.end() != it);
- return it->second.number_retransmissions;
+ return connection->sent_packet_manager_.GetRetransmissionCount(
+ sequence_number);
}
// static
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 9381e05a23..8ba46b8f4a 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -243,8 +243,9 @@ bool PacketSavingConnection::SendOrQueuePacket(
EncryptionLevel level,
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
- QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData retransmittable) {
+ QuicPacketEntropyHash /* entropy_hash */,
+ HasRetransmittableData /* retransmittable */,
+ Force /* forced */) {
packets_.push_back(packet);
QuicEncryptedPacket* encrypted =
framer_.EncryptPacket(level, sequence_number, *packet);
@@ -254,7 +255,7 @@ bool PacketSavingConnection::SendOrQueuePacket(
MockSession::MockSession(QuicConnection* connection, bool is_server)
: QuicSession(connection, DefaultQuicConfig(), is_server) {
- ON_CALL(*this, WriteData(_, _, _, _))
+ ON_CALL(*this, WritevData(_, _, _, _, _))
.WillByDefault(testing::Return(QuicConsumedData(0, false)));
}
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index a90b21270a..64a9d30ada 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -175,15 +175,14 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface {
MockConnectionVisitor();
virtual ~MockConnectionVisitor();
- MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address,
- const IPEndPoint& peer_address,
- const QuicPacketHeader& header,
- const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnStreamFrames, bool(const std::vector<QuicStreamFrame>& frame));
MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer));
- MOCK_METHOD1(OnAck, void(const SequenceNumberSet& acked_packets));
MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation,
+ void(const QuicVersion& version));
private:
DISALLOW_COPY_AND_ASSIGN(MockConnectionVisitor);
@@ -201,7 +200,7 @@ class MockHelper : public QuicConnectionHelperInterface {
MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet,
int* error));
MOCK_METHOD0(IsWriteBlockedDataBuffered, bool());
- MOCK_METHOD1(IsWriteBlocked, bool(int));
+ MOCK_METHOD1(IsWriteBlocked, bool(int stream_id));
virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate);
private:
@@ -235,6 +234,7 @@ class MockConnection : public QuicConnection {
QuicStreamId last_good_stream_id,
const string& reason));
MOCK_METHOD0(OnCanWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
void ProcessUdpPacketInternal(const IPEndPoint& self_address,
const IPEndPoint& peer_address,
@@ -262,7 +262,8 @@ class PacketSavingConnection : public MockConnection {
QuicPacketSequenceNumber sequence_number,
QuicPacket* packet,
QuicPacketEntropyHash entropy_hash,
- HasRetransmittableData has_retransmittable_data) OVERRIDE;
+ HasRetransmittableData has_retransmittable_data,
+ Force forced) OVERRIDE;
std::vector<QuicPacket*> packets_;
std::vector<QuicEncryptedPacket*> encrypted_packets_;
@@ -285,10 +286,11 @@ class MockSession : public QuicSession {
ReliableQuicStream*(QuicStreamId id));
MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*());
MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*());
- MOCK_METHOD4(WriteData, QuicConsumedData(QuicStreamId id,
- base::StringPiece data,
- QuicStreamOffset offset,
- bool fin));
+ MOCK_METHOD5(WritevData, QuicConsumedData(QuicStreamId id,
+ const struct iovec* iov,
+ int count,
+ QuicStreamOffset offset,
+ bool fin));
MOCK_METHOD0(IsHandshakeComplete, bool());
private:
@@ -327,8 +329,9 @@ class MockSendAlgorithm : public SendAlgorithmInterface {
MOCK_METHOD3(OnIncomingAck,
void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta));
MOCK_METHOD1(OnIncomingLoss, void(QuicTime));
- MOCK_METHOD4(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber,
- QuicByteCount, Retransmission));
+ MOCK_METHOD5(SentPacket,
+ bool(QuicTime sent_time, QuicPacketSequenceNumber, QuicByteCount,
+ Retransmission, HasRetransmittableData));
MOCK_METHOD2(AbandoningPacket, void(QuicPacketSequenceNumber sequence_number,
QuicByteCount abandoned_bytes));
MOCK_METHOD4(TimeUntilSend, QuicTime::Delta(QuicTime now, Retransmission,
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index a91d33d24c..22aea47778 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -4,56 +4,317 @@
#include "net/socket/tcp_client_socket.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
namespace net {
-namespace {
+TCPClientSocket::TCPClientSocket(const AddressList& addresses,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source)
+ : socket_(new TCPSocket(net_log, source)),
+ addresses_(addresses),
+ current_address_index_(-1),
+ next_connect_state_(CONNECT_STATE_NONE),
+ previously_disconnected_(false) {
+}
+
+TCPClientSocket::TCPClientSocket(scoped_ptr<TCPSocket> connected_socket,
+ const IPEndPoint& peer_address)
+ : socket_(connected_socket.Pass()),
+ addresses_(AddressList(peer_address)),
+ current_address_index_(0),
+ next_connect_state_(CONNECT_STATE_NONE),
+ previously_disconnected_(false) {
+ DCHECK(socket_);
+
+ socket_->SetDefaultOptionsForClient();
+ use_history_.set_was_ever_connected();
+}
+
+TCPClientSocket::~TCPClientSocket() {
+}
+
+int TCPClientSocket::Bind(const IPEndPoint& address) {
+ if (current_address_index_ >= 0 || bind_address_) {
+ // Cannot bind the socket if we are already connected or connecting.
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+ }
+
+ int result = OK;
+ if (!socket_->IsValid()) {
+ result = OpenSocket(address.GetFamily());
+ if (result != OK)
+ return result;
+ }
+
+ result = socket_->Bind(address);
+ if (result != OK)
+ return result;
+
+ bind_address_.reset(new IPEndPoint(address));
+ return OK;
+}
+
+int TCPClientSocket::Connect(const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ // If connecting or already connected, then just return OK.
+ if (socket_->IsValid() && current_address_index_ >= 0)
+ return OK;
+
+ socket_->StartLoggingMultipleConnectAttempts(addresses_);
+
+ // We will try to connect to each address in addresses_. Start with the
+ // first one in the list.
+ next_connect_state_ = CONNECT_STATE_CONNECT;
+ current_address_index_ = 0;
+
+ int rv = DoConnectLoop(OK);
+ if (rv == ERR_IO_PENDING) {
+ connect_callback_ = callback;
+ } else {
+ socket_->EndLoggingMultipleConnectAttempts(rv);
+ }
+
+ return rv;
+}
-#if defined(OS_LINUX)
+int TCPClientSocket::DoConnectLoop(int result) {
+ DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
-// Checks to see if the system supports TCP FastOpen. Notably, it requires
-// kernel support. Additionally, this checks system configuration to ensure that
-// it's enabled.
-bool SystemSupportsTCPFastOpen() {
- static const base::FilePath::CharType kTCPFastOpenProcFilePath[] =
- "/proc/sys/net/ipv4/tcp_fastopen";
- std::string system_enabled_tcp_fastopen;
- if (!base::ReadFileToString(
- base::FilePath(kTCPFastOpenProcFilePath),
- &system_enabled_tcp_fastopen)) {
- return false;
+ int rv = result;
+ do {
+ ConnectState state = next_connect_state_;
+ next_connect_state_ = CONNECT_STATE_NONE;
+ switch (state) {
+ case CONNECT_STATE_CONNECT:
+ DCHECK_EQ(OK, rv);
+ rv = DoConnect();
+ break;
+ case CONNECT_STATE_CONNECT_COMPLETE:
+ rv = DoConnectComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state " << state;
+ rv = ERR_UNEXPECTED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
+
+ return rv;
+}
+
+int TCPClientSocket::DoConnect() {
+ DCHECK_GE(current_address_index_, 0);
+ DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size()));
+
+ const IPEndPoint& endpoint = addresses_[current_address_index_];
+
+ if (previously_disconnected_) {
+ use_history_.Reset();
+ previously_disconnected_ = false;
}
- // As per http://lxr.linux.no/linux+v3.7.7/include/net/tcp.h#L225
- // TFO_CLIENT_ENABLE is the LSB
- if (system_enabled_tcp_fastopen.empty() ||
- (system_enabled_tcp_fastopen[0] & 0x1) == 0) {
- return false;
+ next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
+
+ if (socket_->IsValid()) {
+ DCHECK(bind_address_);
+ } else {
+ int result = OpenSocket(endpoint.GetFamily());
+ if (result != OK)
+ return result;
+
+ if (bind_address_) {
+ result = socket_->Bind(*bind_address_);
+ if (result != OK) {
+ socket_->Close();
+ return result;
+ }
+ }
+ }
+
+ // |socket_| is owned by this class and the callback won't be run once
+ // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
+ return socket_->Connect(endpoint,
+ base::Bind(&TCPClientSocket::DidCompleteConnect,
+ base::Unretained(this)));
+}
+
+int TCPClientSocket::DoConnectComplete(int result) {
+ if (result == OK) {
+ use_history_.set_was_ever_connected();
+ return OK; // Done!
+ }
+
+ // Close whatever partially connected socket we currently have.
+ DoDisconnect();
+
+ // Try to fall back to the next address in the list.
+ if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) {
+ next_connect_state_ = CONNECT_STATE_CONNECT;
+ ++current_address_index_;
+ return OK;
+ }
+
+ // Otherwise there is nothing to fall back to, so give up.
+ return result;
+}
+
+void TCPClientSocket::Disconnect() {
+ DoDisconnect();
+ current_address_index_ = -1;
+ bind_address_.reset();
+}
+
+void TCPClientSocket::DoDisconnect() {
+ // If connecting or already connected, record that the socket has been
+ // disconnected.
+ previously_disconnected_ = socket_->IsValid() && current_address_index_ >= 0;
+ socket_->Close();
+}
+
+bool TCPClientSocket::IsConnected() const {
+ return socket_->IsConnected();
+}
+
+bool TCPClientSocket::IsConnectedAndIdle() const {
+ return socket_->IsConnectedAndIdle();
+}
+
+int TCPClientSocket::GetPeerAddress(IPEndPoint* address) const {
+ return socket_->GetPeerAddress(address);
+}
+
+int TCPClientSocket::GetLocalAddress(IPEndPoint* address) const {
+ DCHECK(address);
+
+ if (!socket_->IsValid()) {
+ if (bind_address_) {
+ *address = *bind_address_;
+ return OK;
+ }
+ return ERR_SOCKET_NOT_CONNECTED;
}
- return true;
+ return socket_->GetLocalAddress(address);
+}
+
+const BoundNetLog& TCPClientSocket::NetLog() const {
+ return socket_->net_log();
+}
+
+void TCPClientSocket::SetSubresourceSpeculation() {
+ use_history_.set_subresource_speculation();
+}
+
+void TCPClientSocket::SetOmniboxSpeculation() {
+ use_history_.set_omnibox_speculation();
+}
+
+bool TCPClientSocket::WasEverUsed() const {
+ return use_history_.was_used_to_convey_data();
+}
+
+bool TCPClientSocket::UsingTCPFastOpen() const {
+ return socket_->UsingTCPFastOpen();
+}
+
+bool TCPClientSocket::WasNpnNegotiated() const {
+ return false;
}
-#else
+NextProto TCPClientSocket::GetNegotiatedProtocol() const {
+ return kProtoUnknown;
+}
-bool SystemSupportsTCPFastOpen() {
+bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
return false;
}
-#endif
+int TCPClientSocket::Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ // |socket_| is owned by this class and the callback won't be run once
+ // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
+ CompletionCallback read_callback = base::Bind(
+ &TCPClientSocket::DidCompleteReadWrite, base::Unretained(this), callback);
+ int result = socket_->Read(buf, buf_len, read_callback);
+ if (result > 0)
+ use_history_.set_was_used_to_convey_data();
+
+ return result;
+}
+
+int TCPClientSocket::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ // |socket_| is owned by this class and the callback won't be run once
+ // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
+ CompletionCallback write_callback = base::Bind(
+ &TCPClientSocket::DidCompleteReadWrite, base::Unretained(this), callback);
+ int result = socket_->Write(buf, buf_len, write_callback);
+ if (result > 0)
+ use_history_.set_was_used_to_convey_data();
+ return result;
}
-static bool g_tcp_fastopen_enabled = false;
+bool TCPClientSocket::SetReceiveBufferSize(int32 size) {
+ return socket_->SetReceiveBufferSize(size);
+}
+
+bool TCPClientSocket::SetSendBufferSize(int32 size) {
+ return socket_->SetSendBufferSize(size);
+}
+
+bool TCPClientSocket::SetKeepAlive(bool enable, int delay) {
+ return socket_->SetKeepAlive(enable, delay);
+}
-void SetTCPFastOpenEnabled(bool value) {
- g_tcp_fastopen_enabled = value && SystemSupportsTCPFastOpen();
+bool TCPClientSocket::SetNoDelay(bool no_delay) {
+ return socket_->SetNoDelay(no_delay);
}
-bool IsTCPFastOpenEnabled() {
- return g_tcp_fastopen_enabled;
+void TCPClientSocket::DidCompleteConnect(int result) {
+ DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
+ DCHECK_NE(result, ERR_IO_PENDING);
+ DCHECK(!connect_callback_.is_null());
+
+ result = DoConnectLoop(result);
+ if (result != ERR_IO_PENDING) {
+ socket_->EndLoggingMultipleConnectAttempts(result);
+ base::ResetAndReturn(&connect_callback_).Run(result);
+ }
+}
+
+void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback& callback,
+ int result) {
+ if (result > 0)
+ use_history_.set_was_used_to_convey_data();
+
+ callback.Run(result);
+}
+
+int TCPClientSocket::OpenSocket(AddressFamily family) {
+ DCHECK(!socket_->IsValid());
+
+ int result = socket_->Open(family);
+ if (result != OK)
+ return result;
+
+ socket_->SetDefaultOptionsForClient();
+
+ return OK;
}
} // namespace net
diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h
index 8a2c0cd73f..fabcbc1b39 100644
--- a/net/socket/tcp_client_socket.h
+++ b/net/socket/tcp_client_socket.h
@@ -5,30 +5,116 @@
#ifndef NET_SOCKET_TCP_CLIENT_SOCKET_H_
#define NET_SOCKET_TCP_CLIENT_SOCKET_H_
-#include "build/build_config.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/address_list.h"
+#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
-
-#if defined(OS_WIN)
-#include "net/socket/tcp_client_socket_win.h"
-#elif defined(OS_POSIX)
-#include "net/socket/tcp_client_socket_libevent.h"
-#endif
+#include "net/base/net_log.h"
+#include "net/socket/stream_socket.h"
+#include "net/socket/tcp_socket.h"
namespace net {
// A client socket that uses TCP as the transport layer.
-#if defined(OS_WIN)
-typedef TCPClientSocketWin TCPClientSocket;
-#elif defined(OS_POSIX)
-typedef TCPClientSocketLibevent TCPClientSocket;
-#endif
-
-// Enable/disable experimental TCP FastOpen option.
-// Not thread safe. Must be called during initialization/startup only.
-NET_EXPORT void SetTCPFastOpenEnabled(bool value);
-
-// Check if the TCP FastOpen option is enabled.
-bool IsTCPFastOpenEnabled();
+class NET_EXPORT TCPClientSocket : public StreamSocket {
+ public:
+ // The IP address(es) and port number to connect to. The TCP socket will try
+ // each IP address in the list until it succeeds in establishing a
+ // connection.
+ TCPClientSocket(const AddressList& addresses,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+
+ // Adopts the given, connected socket and then acts as if Connect() had been
+ // called. This function is used by TCPServerSocket and for testing.
+ TCPClientSocket(scoped_ptr<TCPSocket> connected_socket,
+ const IPEndPoint& peer_address);
+
+ virtual ~TCPClientSocket();
+
+ // Binds the socket to a local IP address and port.
+ int Bind(const IPEndPoint& address);
+
+ // StreamSocket implementation.
+ virtual int Connect(const CompletionCallback& callback) OVERRIDE;
+ virtual void Disconnect() OVERRIDE;
+ virtual bool IsConnected() const OVERRIDE;
+ virtual bool IsConnectedAndIdle() const OVERRIDE;
+ virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
+ virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
+ virtual const BoundNetLog& NetLog() const OVERRIDE;
+ virtual void SetSubresourceSpeculation() OVERRIDE;
+ virtual void SetOmniboxSpeculation() OVERRIDE;
+ virtual bool WasEverUsed() const OVERRIDE;
+ virtual bool UsingTCPFastOpen() const OVERRIDE;
+ virtual bool WasNpnNegotiated() const OVERRIDE;
+ virtual NextProto GetNegotiatedProtocol() const OVERRIDE;
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
+
+ // Socket implementation.
+ // Multiple outstanding requests are not supported.
+ // Full duplex mode (reading and writing at the same time) is supported.
+ virtual int Read(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int Write(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
+ virtual bool SetSendBufferSize(int32 size) OVERRIDE;
+
+ virtual bool SetKeepAlive(bool enable, int delay);
+ virtual bool SetNoDelay(bool no_delay);
+
+ private:
+ // State machine for connecting the socket.
+ enum ConnectState {
+ CONNECT_STATE_CONNECT,
+ CONNECT_STATE_CONNECT_COMPLETE,
+ CONNECT_STATE_NONE,
+ };
+
+ // State machine used by Connect().
+ int DoConnectLoop(int result);
+ int DoConnect();
+ int DoConnectComplete(int result);
+
+ // Helper used by Disconnect(), which disconnects minus resetting
+ // current_address_index_ and bind_address_.
+ void DoDisconnect();
+
+ void DidCompleteConnect(int result);
+ void DidCompleteReadWrite(const CompletionCallback& callback, int result);
+
+ int OpenSocket(AddressFamily family);
+
+ scoped_ptr<TCPSocket> socket_;
+
+ // Local IP address and port we are bound to. Set to NULL if Bind()
+ // wasn't called (in that case OS chooses address/port).
+ scoped_ptr<IPEndPoint> bind_address_;
+
+ // The list of addresses we should try in order to establish a connection.
+ AddressList addresses_;
+
+ // Where we are in above list. Set to -1 if uninitialized.
+ int current_address_index_;
+
+ // External callback; called when connect is complete.
+ CompletionCallback connect_callback_;
+
+ // The next state for the Connect() state machine.
+ ConnectState next_connect_state_;
+
+ // This socket was previously disconnected and has not been re-connected.
+ bool previously_disconnected_;
+
+ // Record of connectivity and transmissions, for use in speculative connection
+ // histograms.
+ UseHistory use_history_;
+
+ DISALLOW_COPY_AND_ASSIGN(TCPClientSocket);
+};
} // namespace net
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc
deleted file mode 100644
index cbcd25fb38..0000000000
--- a/net/socket/tcp_client_socket_libevent.cc
+++ /dev/null
@@ -1,830 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/socket/tcp_client_socket.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/tcp.h>
-#if defined(OS_POSIX)
-#include <netinet/in.h>
-#endif
-
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/stats_counters.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/string_util.h"
-#include "net/base/connection_type_histograms.h"
-#include "net/base/io_buffer.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/base/net_util.h"
-#include "net/base/network_change_notifier.h"
-#include "net/socket/socket_descriptor.h"
-#include "net/socket/socket_net_log_params.h"
-
-// If we don't have a definition for TCPI_OPT_SYN_DATA, create one.
-#ifndef TCPI_OPT_SYN_DATA
-#define TCPI_OPT_SYN_DATA 32
-#endif
-
-namespace net {
-
-namespace {
-
-const int kTCPKeepAliveSeconds = 45;
-
-// SetTCPNoDelay turns on/off buffering in the kernel. By default, TCP sockets
-// will wait up to 200ms for more data to complete a packet before transmitting.
-// After calling this function, the kernel will not wait. See TCP_NODELAY in
-// `man 7 tcp`.
-bool SetTCPNoDelay(int fd, bool no_delay) {
- int on = no_delay ? 1 : 0;
- int error = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on,
- sizeof(on));
- return error == 0;
-}
-
-// SetTCPKeepAlive sets SO_KEEPALIVE.
-bool SetTCPKeepAlive(int fd, bool enable, int delay) {
- int on = enable ? 1 : 0;
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) {
- PLOG(ERROR) << "Failed to set SO_KEEPALIVE on fd: " << fd;
- return false;
- }
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- // Set seconds until first TCP keep alive.
- if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) {
- PLOG(ERROR) << "Failed to set TCP_KEEPIDLE on fd: " << fd;
- return false;
- }
- // Set seconds between TCP keep alives.
- if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &delay, sizeof(delay))) {
- PLOG(ERROR) << "Failed to set TCP_KEEPINTVL on fd: " << fd;
- return false;
- }
-#endif
- return true;
-}
-
-// Sets socket parameters. Returns the OS error code (or 0 on
-// success).
-int SetupSocket(int socket) {
- if (SetNonBlocking(socket))
- return errno;
-
- // This mirrors the behaviour on Windows. See the comment in
- // tcp_client_socket_win.cc after searching for "NODELAY".
- SetTCPNoDelay(socket, true); // If SetTCPNoDelay fails, we don't care.
- SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds);
-
- return 0;
-}
-
-// Creates a new socket and sets default parameters for it. Returns
-// the OS error code (or 0 on success).
-int CreateSocket(int family, int* socket) {
- *socket = CreatePlatformSocket(family, SOCK_STREAM, IPPROTO_TCP);
- if (*socket == kInvalidSocket)
- return errno;
- int error = SetupSocket(*socket);
- if (error) {
- if (HANDLE_EINTR(close(*socket)) < 0)
- PLOG(ERROR) << "close";
- *socket = kInvalidSocket;
- return error;
- }
- return 0;
-}
-
-int MapConnectError(int os_error) {
- switch (os_error) {
- case EACCES:
- return ERR_NETWORK_ACCESS_DENIED;
- case ETIMEDOUT:
- return ERR_CONNECTION_TIMED_OUT;
- default: {
- int net_error = MapSystemError(os_error);
- if (net_error == ERR_FAILED)
- return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
-
- // Give a more specific error when the user is offline.
- if (net_error == ERR_ADDRESS_UNREACHABLE &&
- NetworkChangeNotifier::IsOffline()) {
- return ERR_INTERNET_DISCONNECTED;
- }
- return net_error;
- }
- }
-}
-
-} // namespace
-
-//-----------------------------------------------------------------------------
-
-TCPClientSocketLibevent::TCPClientSocketLibevent(
- const AddressList& addresses,
- net::NetLog* net_log,
- const net::NetLog::Source& source)
- : socket_(kInvalidSocket),
- bound_socket_(kInvalidSocket),
- addresses_(addresses),
- current_address_index_(-1),
- read_watcher_(this),
- write_watcher_(this),
- next_connect_state_(CONNECT_STATE_NONE),
- connect_os_error_(0),
- net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)),
- previously_disconnected_(false),
- use_tcp_fastopen_(IsTCPFastOpenEnabled()),
- tcp_fastopen_connected_(false),
- fast_open_status_(FAST_OPEN_STATUS_UNKNOWN) {
- net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
- source.ToEventParametersCallback());
-}
-
-TCPClientSocketLibevent::~TCPClientSocketLibevent() {
- Disconnect();
- net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
- if (tcp_fastopen_connected_) {
- UMA_HISTOGRAM_ENUMERATION("Net.TcpFastOpenSocketConnection",
- fast_open_status_, FAST_OPEN_MAX_VALUE);
- }
-}
-
-int TCPClientSocketLibevent::AdoptSocket(int socket) {
- DCHECK_EQ(socket_, kInvalidSocket);
-
- int error = SetupSocket(socket);
- if (error)
- return MapSystemError(error);
-
- socket_ = socket;
-
- // This is to make GetPeerAddress() work. It's up to the caller ensure
- // that |address_| contains a reasonable address for this
- // socket. (i.e. at least match IPv4 vs IPv6!).
- current_address_index_ = 0;
- use_history_.set_was_ever_connected();
-
- return OK;
-}
-
-int TCPClientSocketLibevent::Bind(const IPEndPoint& address) {
- if (current_address_index_ >= 0 || bind_address_.get()) {
- // Cannot bind the socket if we are already bound connected or
- // connecting.
- return ERR_UNEXPECTED;
- }
-
- SockaddrStorage storage;
- if (!address.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
-
- // Create |bound_socket_| and try to bind it to |address|.
- int error = CreateSocket(address.GetSockAddrFamily(), &bound_socket_);
- if (error)
- return MapSystemError(error);
-
- if (HANDLE_EINTR(bind(bound_socket_, storage.addr, storage.addr_len))) {
- error = errno;
- if (HANDLE_EINTR(close(bound_socket_)) < 0)
- PLOG(ERROR) << "close";
- bound_socket_ = kInvalidSocket;
- return MapSystemError(error);
- }
-
- bind_address_.reset(new IPEndPoint(address));
-
- return 0;
-}
-
-int TCPClientSocketLibevent::Connect(const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
-
- // If already connected, then just return OK.
- if (socket_ != kInvalidSocket)
- return OK;
-
- base::StatsCounter connects("tcp.connect");
- connects.Increment();
-
- DCHECK(!waiting_connect());
-
- net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
- addresses_.CreateNetLogCallback());
-
- // We will try to connect to each address in addresses_. Start with the
- // first one in the list.
- next_connect_state_ = CONNECT_STATE_CONNECT;
- current_address_index_ = 0;
-
- int rv = DoConnectLoop(OK);
- if (rv == ERR_IO_PENDING) {
- // Synchronous operation not supported.
- DCHECK(!callback.is_null());
- write_callback_ = callback;
- } else {
- LogConnectCompletion(rv);
- }
-
- return rv;
-}
-
-int TCPClientSocketLibevent::DoConnectLoop(int result) {
- DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
-
- int rv = result;
- do {
- ConnectState state = next_connect_state_;
- next_connect_state_ = CONNECT_STATE_NONE;
- switch (state) {
- case CONNECT_STATE_CONNECT:
- DCHECK_EQ(OK, rv);
- rv = DoConnect();
- break;
- case CONNECT_STATE_CONNECT_COMPLETE:
- rv = DoConnectComplete(rv);
- break;
- default:
- LOG(DFATAL) << "bad state";
- rv = ERR_UNEXPECTED;
- break;
- }
- } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
-
- return rv;
-}
-
-int TCPClientSocketLibevent::DoConnect() {
- DCHECK_GE(current_address_index_, 0);
- DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size()));
- DCHECK_EQ(0, connect_os_error_);
-
- const IPEndPoint& endpoint = addresses_[current_address_index_];
-
- if (previously_disconnected_) {
- use_history_.Reset();
- previously_disconnected_ = false;
- }
-
- net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
- CreateNetLogIPEndPointCallback(&endpoint));
-
- next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
-
- if (bound_socket_ != kInvalidSocket) {
- DCHECK(bind_address_.get());
- socket_ = bound_socket_;
- bound_socket_ = kInvalidSocket;
- } else {
- // Create a non-blocking socket.
- connect_os_error_ = CreateSocket(endpoint.GetSockAddrFamily(), &socket_);
- if (connect_os_error_)
- return MapSystemError(connect_os_error_);
-
- if (bind_address_.get()) {
- SockaddrStorage storage;
- if (!bind_address_->ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
- if (HANDLE_EINTR(bind(socket_, storage.addr, storage.addr_len)))
- return MapSystemError(errno);
- }
- }
-
- // Connect the socket.
- if (!use_tcp_fastopen_) {
- SockaddrStorage storage;
- if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
-
- if (!HANDLE_EINTR(connect(socket_, storage.addr, storage.addr_len))) {
- // Connected without waiting!
- return OK;
- }
- } else {
- // With TCP FastOpen, we pretend that the socket is connected.
- DCHECK(!tcp_fastopen_connected_);
- return OK;
- }
-
- // Check if the connect() failed synchronously.
- connect_os_error_ = errno;
- if (connect_os_error_ != EINPROGRESS)
- return MapConnectError(connect_os_error_);
-
- // Otherwise the connect() is going to complete asynchronously, so watch
- // for its completion.
- if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
- socket_, true, base::MessageLoopForIO::WATCH_WRITE,
- &write_socket_watcher_, &write_watcher_)) {
- connect_os_error_ = errno;
- DVLOG(1) << "WatchFileDescriptor failed: " << connect_os_error_;
- return MapSystemError(connect_os_error_);
- }
-
- return ERR_IO_PENDING;
-}
-
-int TCPClientSocketLibevent::DoConnectComplete(int result) {
- // Log the end of this attempt (and any OS error it threw).
- int os_error = connect_os_error_;
- connect_os_error_ = 0;
- if (result != OK) {
- net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
- NetLog::IntegerCallback("os_error", os_error));
- } else {
- net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT);
- }
-
- if (result == OK) {
- write_socket_watcher_.StopWatchingFileDescriptor();
- use_history_.set_was_ever_connected();
- return OK; // Done!
- }
-
- // Close whatever partially connected socket we currently have.
- DoDisconnect();
-
- // Try to fall back to the next address in the list.
- if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) {
- next_connect_state_ = CONNECT_STATE_CONNECT;
- ++current_address_index_;
- return OK;
- }
-
- // Otherwise there is nothing to fall back to, so give up.
- return result;
-}
-
-void TCPClientSocketLibevent::Disconnect() {
- DCHECK(CalledOnValidThread());
-
- DoDisconnect();
- current_address_index_ = -1;
- bind_address_.reset();
-}
-
-void TCPClientSocketLibevent::DoDisconnect() {
- if (socket_ == kInvalidSocket)
- return;
-
- bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
- DCHECK(ok);
- ok = write_socket_watcher_.StopWatchingFileDescriptor();
- DCHECK(ok);
- if (HANDLE_EINTR(close(socket_)) < 0)
- PLOG(ERROR) << "close";
- socket_ = kInvalidSocket;
- previously_disconnected_ = true;
-}
-
-bool TCPClientSocketLibevent::IsConnected() const {
- DCHECK(CalledOnValidThread());
-
- if (socket_ == kInvalidSocket || waiting_connect())
- return false;
-
- if (use_tcp_fastopen_ && !tcp_fastopen_connected_) {
- // With TCP FastOpen, we pretend that the socket is connected.
- // This allows GetPeerAddress() to return current_ai_ as the peer
- // address. Since we don't fail over to the next address if
- // sendto() fails, current_ai_ is the only possible peer address.
- CHECK_LT(current_address_index_, static_cast<int>(addresses_.size()));
- return true;
- }
-
- // Check if connection is alive.
- char c;
- int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
- if (rv == 0)
- return false;
- if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
- return false;
-
- return true;
-}
-
-bool TCPClientSocketLibevent::IsConnectedAndIdle() const {
- DCHECK(CalledOnValidThread());
-
- if (socket_ == kInvalidSocket || waiting_connect())
- return false;
-
- // TODO(wtc): should we also handle the TCP FastOpen case here,
- // as we do in IsConnected()?
-
- // Check if connection is alive and we haven't received any data
- // unexpectedly.
- char c;
- int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
- if (rv >= 0)
- return false;
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- return false;
-
- return true;
-}
-
-int TCPClientSocketLibevent::Read(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK_NE(kInvalidSocket, socket_);
- DCHECK(!waiting_connect());
- DCHECK(read_callback_.is_null());
- // Synchronous operation not supported
- DCHECK(!callback.is_null());
- DCHECK_GT(buf_len, 0);
-
- int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
- if (nread >= 0) {
- base::StatsCounter read_bytes("tcp.read_bytes");
- read_bytes.Add(nread);
- if (nread > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, nread,
- buf->data());
- RecordFastOpenStatus();
- return nread;
- }
- if (errno != EAGAIN && errno != EWOULDBLOCK) {
- int net_error = MapSystemError(errno);
- net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
- CreateNetLogSocketErrorCallback(net_error, errno));
- return net_error;
- }
-
- if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
- socket_, true, base::MessageLoopForIO::WATCH_READ,
- &read_socket_watcher_, &read_watcher_)) {
- DVLOG(1) << "WatchFileDescriptor failed on read, errno " << errno;
- return MapSystemError(errno);
- }
-
- read_buf_ = buf;
- read_buf_len_ = buf_len;
- read_callback_ = callback;
- return ERR_IO_PENDING;
-}
-
-int TCPClientSocketLibevent::Write(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK_NE(kInvalidSocket, socket_);
- DCHECK(!waiting_connect());
- DCHECK(write_callback_.is_null());
- // Synchronous operation not supported
- DCHECK(!callback.is_null());
- DCHECK_GT(buf_len, 0);
-
- int nwrite = InternalWrite(buf, buf_len);
- if (nwrite >= 0) {
- base::StatsCounter write_bytes("tcp.write_bytes");
- write_bytes.Add(nwrite);
- if (nwrite > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, nwrite,
- buf->data());
- return nwrite;
- }
- if (errno != EAGAIN && errno != EWOULDBLOCK) {
- int net_error = MapSystemError(errno);
- net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
- CreateNetLogSocketErrorCallback(net_error, errno));
- return net_error;
- }
-
- if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
- socket_, true, base::MessageLoopForIO::WATCH_WRITE,
- &write_socket_watcher_, &write_watcher_)) {
- DVLOG(1) << "WatchFileDescriptor failed on write, errno " << errno;
- return MapSystemError(errno);
- }
-
- write_buf_ = buf;
- write_buf_len_ = buf_len;
- write_callback_ = callback;
- return ERR_IO_PENDING;
-}
-
-int TCPClientSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) {
- int nwrite;
- if (use_tcp_fastopen_ && !tcp_fastopen_connected_) {
- SockaddrStorage storage;
- if (!addresses_[current_address_index_].ToSockAddr(storage.addr,
- &storage.addr_len)) {
- errno = EINVAL;
- return -1;
- }
-
- int flags = 0x20000000; // Magic flag to enable TCP_FASTOPEN.
-#if defined(OS_LINUX)
- // sendto() will fail with EPIPE when the system doesn't support TCP Fast
- // Open. Theoretically that shouldn't happen since the caller should check
- // for system support on startup, but users may dynamically disable TCP Fast
- // Open via sysctl.
- flags |= MSG_NOSIGNAL;
-#endif // defined(OS_LINUX)
- nwrite = HANDLE_EINTR(sendto(socket_,
- buf->data(),
- buf_len,
- flags,
- storage.addr,
- storage.addr_len));
- tcp_fastopen_connected_ = true;
-
- if (nwrite < 0) {
- DCHECK_NE(EPIPE, errno);
-
- // If errno == EINPROGRESS, that means the kernel didn't have a cookie
- // and would block. The kernel is internally doing a connect() though.
- // Remap EINPROGRESS to EAGAIN so we treat this the same as our other
- // asynchronous cases. Note that the user buffer has not been copied to
- // kernel space.
- if (errno == EINPROGRESS) {
- errno = EAGAIN;
- fast_open_status_ = FAST_OPEN_SLOW_CONNECT_RETURN;
- } else {
- fast_open_status_ = FAST_OPEN_ERROR;
- }
- } else {
- fast_open_status_ = FAST_OPEN_FAST_CONNECT_RETURN;
- }
- } else {
- nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
- }
- return nwrite;
-}
-
-bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) {
- DCHECK(CalledOnValidThread());
- int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
- reinterpret_cast<const char*>(&size),
- sizeof(size));
- DCHECK(!rv) << "Could not set socket receive buffer size: " << errno;
- return rv == 0;
-}
-
-bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) {
- DCHECK(CalledOnValidThread());
- int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
- reinterpret_cast<const char*>(&size),
- sizeof(size));
- DCHECK(!rv) << "Could not set socket send buffer size: " << errno;
- return rv == 0;
-}
-
-bool TCPClientSocketLibevent::SetKeepAlive(bool enable, int delay) {
- int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_;
- return SetTCPKeepAlive(socket, enable, delay);
-}
-
-bool TCPClientSocketLibevent::SetNoDelay(bool no_delay) {
- int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_;
- return SetTCPNoDelay(socket, no_delay);
-}
-
-void TCPClientSocketLibevent::ReadWatcher::OnFileCanReadWithoutBlocking(int) {
- socket_->RecordFastOpenStatus();
- if (!socket_->read_callback_.is_null())
- socket_->DidCompleteRead();
-}
-
-void TCPClientSocketLibevent::WriteWatcher::OnFileCanWriteWithoutBlocking(int) {
- if (socket_->waiting_connect()) {
- socket_->DidCompleteConnect();
- } else if (!socket_->write_callback_.is_null()) {
- socket_->DidCompleteWrite();
- }
-}
-
-void TCPClientSocketLibevent::LogConnectCompletion(int net_error) {
- if (net_error == OK)
- UpdateConnectionTypeHistograms(CONNECTION_ANY);
-
- if (net_error != OK) {
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error);
- return;
- }
-
- SockaddrStorage storage;
- int rv = getsockname(socket_, storage.addr, &storage.addr_len);
- if (rv != 0) {
- PLOG(ERROR) << "getsockname() [rv: " << rv << "] error: ";
- NOTREACHED();
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv);
- return;
- }
-
- net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT,
- CreateNetLogSourceAddressCallback(storage.addr,
- storage.addr_len));
-}
-
-void TCPClientSocketLibevent::DoReadCallback(int rv) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(!read_callback_.is_null());
-
- // since Run may result in Read being called, clear read_callback_ up front.
- CompletionCallback c = read_callback_;
- read_callback_.Reset();
- c.Run(rv);
-}
-
-void TCPClientSocketLibevent::DoWriteCallback(int rv) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(!write_callback_.is_null());
-
- // since Run may result in Write being called, clear write_callback_ up front.
- CompletionCallback c = write_callback_;
- write_callback_.Reset();
- c.Run(rv);
-}
-
-void TCPClientSocketLibevent::DidCompleteConnect() {
- DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
-
- // Get the error that connect() completed with.
- int os_error = 0;
- socklen_t len = sizeof(os_error);
- if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0)
- os_error = errno;
-
- // TODO(eroman): Is this check really necessary?
- if (os_error == EINPROGRESS || os_error == EALREADY) {
- NOTREACHED(); // This indicates a bug in libevent or our code.
- return;
- }
-
- connect_os_error_ = os_error;
- int rv = DoConnectLoop(MapConnectError(os_error));
- if (rv != ERR_IO_PENDING) {
- LogConnectCompletion(rv);
- DoWriteCallback(rv);
- }
-}
-
-void TCPClientSocketLibevent::DidCompleteRead() {
- int bytes_transferred;
- bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(),
- read_buf_len_));
-
- int result;
- if (bytes_transferred >= 0) {
- result = bytes_transferred;
- base::StatsCounter read_bytes("tcp.read_bytes");
- read_bytes.Add(bytes_transferred);
- if (bytes_transferred > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, result,
- read_buf_->data());
- } else {
- result = MapSystemError(errno);
- if (result != ERR_IO_PENDING) {
- net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
- CreateNetLogSocketErrorCallback(result, errno));
- }
- }
-
- if (result != ERR_IO_PENDING) {
- read_buf_ = NULL;
- read_buf_len_ = 0;
- bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
- DCHECK(ok);
- DoReadCallback(result);
- }
-}
-
-void TCPClientSocketLibevent::DidCompleteWrite() {
- int bytes_transferred;
- bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(),
- write_buf_len_));
-
- int result;
- if (bytes_transferred >= 0) {
- result = bytes_transferred;
- base::StatsCounter write_bytes("tcp.write_bytes");
- write_bytes.Add(bytes_transferred);
- if (bytes_transferred > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, result,
- write_buf_->data());
- } else {
- result = MapSystemError(errno);
- if (result != ERR_IO_PENDING) {
- net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
- CreateNetLogSocketErrorCallback(result, errno));
- }
- }
-
- if (result != ERR_IO_PENDING) {
- write_buf_ = NULL;
- write_buf_len_ = 0;
- write_socket_watcher_.StopWatchingFileDescriptor();
- DoWriteCallback(result);
- }
-}
-
-int TCPClientSocketLibevent::GetPeerAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
- if (!IsConnected())
- return ERR_SOCKET_NOT_CONNECTED;
- *address = addresses_[current_address_index_];
- return OK;
-}
-
-int TCPClientSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
- if (socket_ == kInvalidSocket) {
- if (bind_address_.get()) {
- *address = *bind_address_;
- return OK;
- }
- return ERR_SOCKET_NOT_CONNECTED;
- }
-
- SockaddrStorage storage;
- if (getsockname(socket_, storage.addr, &storage.addr_len))
- return MapSystemError(errno);
- if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
-
- return OK;
-}
-
-void TCPClientSocketLibevent::RecordFastOpenStatus() {
- if (use_tcp_fastopen_ &&
- (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN ||
- fast_open_status_ == FAST_OPEN_SLOW_CONNECT_RETURN)) {
- DCHECK_NE(FAST_OPEN_STATUS_UNKNOWN, fast_open_status_);
- bool getsockopt_success(false);
- bool server_acked_data(false);
-#if defined(TCP_INFO)
- // Probe to see the if the socket used TCP Fast Open.
- tcp_info info;
- socklen_t info_len = sizeof(tcp_info);
- getsockopt_success =
- getsockopt(socket_, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0 &&
- info_len == sizeof(tcp_info);
- server_acked_data = getsockopt_success &&
- (info.tcpi_options & TCPI_OPT_SYN_DATA);
-#endif
- if (getsockopt_success) {
- if (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN) {
- fast_open_status_ = (server_acked_data ? FAST_OPEN_SYN_DATA_ACK :
- FAST_OPEN_SYN_DATA_NACK);
- } else {
- fast_open_status_ = (server_acked_data ? FAST_OPEN_NO_SYN_DATA_ACK :
- FAST_OPEN_NO_SYN_DATA_NACK);
- }
- } else {
- fast_open_status_ = (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN ?
- FAST_OPEN_SYN_DATA_FAILED :
- FAST_OPEN_NO_SYN_DATA_FAILED);
- }
- }
-}
-
-const BoundNetLog& TCPClientSocketLibevent::NetLog() const {
- return net_log_;
-}
-
-void TCPClientSocketLibevent::SetSubresourceSpeculation() {
- use_history_.set_subresource_speculation();
-}
-
-void TCPClientSocketLibevent::SetOmniboxSpeculation() {
- use_history_.set_omnibox_speculation();
-}
-
-bool TCPClientSocketLibevent::WasEverUsed() const {
- return use_history_.was_used_to_convey_data();
-}
-
-bool TCPClientSocketLibevent::UsingTCPFastOpen() const {
- return use_tcp_fastopen_;
-}
-
-bool TCPClientSocketLibevent::WasNpnNegotiated() const {
- return false;
-}
-
-NextProto TCPClientSocketLibevent::GetNegotiatedProtocol() const {
- return kProtoUnknown;
-}
-
-bool TCPClientSocketLibevent::GetSSLInfo(SSLInfo* ssl_info) {
- return false;
-}
-
-} // namespace net
diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h
deleted file mode 100644
index e5a0d8deab..0000000000
--- a/net/socket/tcp_client_socket_libevent.h
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_SOCKET_TCP_CLIENT_SOCKET_LIBEVENT_H_
-#define NET_SOCKET_TCP_CLIENT_SOCKET_LIBEVENT_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/non_thread_safe.h"
-#include "net/base/address_list.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_log.h"
-#include "net/socket/stream_socket.h"
-
-namespace net {
-
-class BoundNetLog;
-
-// A client socket that uses TCP as the transport layer.
-class NET_EXPORT_PRIVATE TCPClientSocketLibevent : public StreamSocket,
- public base::NonThreadSafe {
- public:
- // The IP address(es) and port number to connect to. The TCP socket will try
- // each IP address in the list until it succeeds in establishing a
- // connection.
- TCPClientSocketLibevent(const AddressList& addresses,
- net::NetLog* net_log,
- const net::NetLog::Source& source);
-
- virtual ~TCPClientSocketLibevent();
-
- // AdoptSocket causes the given, connected socket to be adopted as a TCP
- // socket. This object must not be connected. This object takes ownership of
- // the given socket and then acts as if Connect() had been called. This
- // function is used by TCPServerSocket() to adopt accepted connections
- // and for testing.
- int AdoptSocket(int socket);
-
- // Binds the socket to a local IP address and port.
- int Bind(const IPEndPoint& address);
-
- // StreamSocket implementation.
- virtual int Connect(const CompletionCallback& callback) OVERRIDE;
- virtual void Disconnect() OVERRIDE;
- virtual bool IsConnected() const OVERRIDE;
- virtual bool IsConnectedAndIdle() const OVERRIDE;
- virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE;
- virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE;
- virtual const BoundNetLog& NetLog() const OVERRIDE;
- virtual void SetSubresourceSpeculation() OVERRIDE;
- virtual void SetOmniboxSpeculation() OVERRIDE;
- virtual bool WasEverUsed() const OVERRIDE;
- virtual bool UsingTCPFastOpen() const OVERRIDE;
- virtual bool WasNpnNegotiated() const OVERRIDE;
- virtual NextProto GetNegotiatedProtocol() const OVERRIDE;
- virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
-
- // Socket implementation.
- // Multiple outstanding requests are not supported.
- // Full duplex mode (reading and writing at the same time) is supported
- virtual int Read(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) OVERRIDE;
- virtual int Write(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) OVERRIDE;
- virtual bool SetReceiveBufferSize(int32 size) OVERRIDE;
- virtual bool SetSendBufferSize(int32 size) OVERRIDE;
-
- virtual bool SetKeepAlive(bool enable, int delay);
- virtual bool SetNoDelay(bool no_delay);
-
- private:
- // State machine for connecting the socket.
- enum ConnectState {
- CONNECT_STATE_CONNECT,
- CONNECT_STATE_CONNECT_COMPLETE,
- CONNECT_STATE_NONE,
- };
-
- // States that a fast open socket attempt can result in.
- enum FastOpenStatus {
- FAST_OPEN_STATUS_UNKNOWN,
-
- // The initial fast open connect attempted returned synchronously,
- // indicating that we had and sent a cookie along with the initial data.
- FAST_OPEN_FAST_CONNECT_RETURN,
-
- // The initial fast open connect attempted returned asynchronously,
- // indicating that we did not have a cookie for the server.
- FAST_OPEN_SLOW_CONNECT_RETURN,
-
- // Some other error occurred on connection, so we couldn't tell if
- // fast open would have worked.
- FAST_OPEN_ERROR,
-
- // An attempt to do a fast open succeeded immediately
- // (FAST_OPEN_FAST_CONNECT_RETURN) and we later confirmed that the server
- // had acked the data we sent.
- FAST_OPEN_SYN_DATA_ACK,
-
- // An attempt to do a fast open succeeded immediately
- // (FAST_OPEN_FAST_CONNECT_RETURN) and we later confirmed that the server
- // had nacked the data we sent.
- FAST_OPEN_SYN_DATA_NACK,
-
- // An attempt to do a fast open succeeded immediately
- // (FAST_OPEN_FAST_CONNECT_RETURN) and our probe to determine if the
- // socket was using fast open failed.
- FAST_OPEN_SYN_DATA_FAILED,
-
- // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
- // and we later confirmed that the server had acked initial data. This
- // should never happen (we didn't send data, so it shouldn't have
- // been acked).
- FAST_OPEN_NO_SYN_DATA_ACK,
-
- // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
- // and we later discovered that the server had nacked initial data. This
- // is the expected case results for FAST_OPEN_SLOW_CONNECT_RETURN.
- FAST_OPEN_NO_SYN_DATA_NACK,
-
- // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
- // and our later probe for ack/nack state failed.
- FAST_OPEN_NO_SYN_DATA_FAILED,
-
- FAST_OPEN_MAX_VALUE
- };
-
- class ReadWatcher : public base::MessageLoopForIO::Watcher {
- public:
- explicit ReadWatcher(TCPClientSocketLibevent* socket) : socket_(socket) {}
-
- // MessageLoopForIO::Watcher methods
-
- virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE;
-
- virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {}
-
- private:
- TCPClientSocketLibevent* const socket_;
-
- DISALLOW_COPY_AND_ASSIGN(ReadWatcher);
- };
-
- class WriteWatcher : public base::MessageLoopForIO::Watcher {
- public:
- explicit WriteWatcher(TCPClientSocketLibevent* socket) : socket_(socket) {}
-
- // MessageLoopForIO::Watcher implementation.
- virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {}
- virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE;
-
- private:
- TCPClientSocketLibevent* const socket_;
-
- DISALLOW_COPY_AND_ASSIGN(WriteWatcher);
- };
-
- // State machine used by Connect().
- int DoConnectLoop(int result);
- int DoConnect();
- int DoConnectComplete(int result);
-
- // Helper used by Disconnect(), which disconnects minus the logging and
- // resetting of current_address_index_.
- void DoDisconnect();
-
- void DoReadCallback(int rv);
- void DoWriteCallback(int rv);
- void DidCompleteRead();
- void DidCompleteWrite();
- void DidCompleteConnect();
-
- // Returns true if a Connect() is in progress.
- bool waiting_connect() const {
- return next_connect_state_ != CONNECT_STATE_NONE;
- }
-
- // Helper to add a TCP_CONNECT (end) event to the NetLog.
- void LogConnectCompletion(int net_error);
-
- // Internal function to write to a socket.
- int InternalWrite(IOBuffer* buf, int buf_len);
-
- // Called when the socket is known to be in a connected state.
- void RecordFastOpenStatus();
-
- int socket_;
-
- // Local IP address and port we are bound to. Set to NULL if Bind()
- // was't called (in that cases OS chooses address/port).
- scoped_ptr<IPEndPoint> bind_address_;
-
- // Stores bound socket between Bind() and Connect() calls.
- int bound_socket_;
-
- // The list of addresses we should try in order to establish a connection.
- AddressList addresses_;
-
- // Where we are in above list. Set to -1 if uninitialized.
- int current_address_index_;
-
- // The socket's libevent wrappers
- base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
- base::MessageLoopForIO::FileDescriptorWatcher write_socket_watcher_;
-
- // The corresponding watchers for reads and writes.
- ReadWatcher read_watcher_;
- WriteWatcher write_watcher_;
-
- // The buffer used by OnSocketReady to retry Read requests
- scoped_refptr<IOBuffer> read_buf_;
- int read_buf_len_;
-
- // The buffer used by OnSocketReady to retry Write requests
- scoped_refptr<IOBuffer> write_buf_;
- int write_buf_len_;
-
- // External callback; called when read is complete.
- CompletionCallback read_callback_;
-
- // External callback; called when write is complete.
- CompletionCallback write_callback_;
-
- // The next state for the Connect() state machine.
- ConnectState next_connect_state_;
-
- // The OS error that CONNECT_STATE_CONNECT last completed with.
- int connect_os_error_;
-
- BoundNetLog net_log_;
-
- // This socket was previously disconnected and has not been re-connected.
- bool previously_disconnected_;
-
- // Record of connectivity and transmissions, for use in speculative connection
- // histograms.
- UseHistory use_history_;
-
- // Enables experimental TCP FastOpen option.
- const bool use_tcp_fastopen_;
-
- // True when TCP FastOpen is in use and we have done the connect.
- bool tcp_fastopen_connected_;
-
- enum FastOpenStatus fast_open_status_;
-
- DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent);
-};
-
-} // namespace net
-
-#endif // NET_SOCKET_TCP_CLIENT_SOCKET_LIBEVENT_H_
diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc
deleted file mode 100644
index f1334e78a4..0000000000
--- a/net/socket/tcp_client_socket_win.cc
+++ /dev/null
@@ -1,956 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/socket/tcp_client_socket_win.h"
-
-#include <mstcpip.h>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/metrics/stats_counters.h"
-#include "base/strings/string_util.h"
-#include "base/win/object_watcher.h"
-#include "base/win/windows_version.h"
-#include "net/base/connection_type_histograms.h"
-#include "net/base/io_buffer.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_log.h"
-#include "net/base/net_util.h"
-#include "net/base/network_change_notifier.h"
-#include "net/base/winsock_init.h"
-#include "net/base/winsock_util.h"
-#include "net/socket/socket_descriptor.h"
-#include "net/socket/socket_net_log_params.h"
-
-namespace net {
-
-namespace {
-
-const int kTCPKeepAliveSeconds = 45;
-bool g_disable_overlapped_reads = false;
-
-bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) {
- int rv = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
- reinterpret_cast<const char*>(&size), sizeof(size));
- DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError();
- return rv == 0;
-}
-
-bool SetSocketSendBufferSize(SOCKET socket, int32 size) {
- int rv = setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
- reinterpret_cast<const char*>(&size), sizeof(size));
- DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError();
- return rv == 0;
-}
-
-// Disable Nagle.
-// The Nagle implementation on windows is governed by RFC 896. The idea
-// behind Nagle is to reduce small packets on the network. When Nagle is
-// enabled, if a partial packet has been sent, the TCP stack will disallow
-// further *partial* packets until an ACK has been received from the other
-// side. Good applications should always strive to send as much data as
-// possible and avoid partial-packet sends. However, in most real world
-// applications, there are edge cases where this does not happen, and two
-// partial packets may be sent back to back. For a browser, it is NEVER
-// a benefit to delay for an RTT before the second packet is sent.
-//
-// As a practical example in Chromium today, consider the case of a small
-// POST. I have verified this:
-// Client writes 649 bytes of header (partial packet #1)
-// Client writes 50 bytes of POST data (partial packet #2)
-// In the above example, with Nagle, a RTT delay is inserted between these
-// two sends due to nagle. RTTs can easily be 100ms or more. The best
-// fix is to make sure that for POSTing data, we write as much data as
-// possible and minimize partial packets. We will fix that. But disabling
-// Nagle also ensure we don't run into this delay in other edge cases.
-// See also:
-// http://technet.microsoft.com/en-us/library/bb726981.aspx
-bool DisableNagle(SOCKET socket, bool disable) {
- BOOL val = disable ? TRUE : FALSE;
- int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
- reinterpret_cast<const char*>(&val),
- sizeof(val));
- DCHECK(!rv) << "Could not disable nagle";
- return rv == 0;
-}
-
-// Enable TCP Keep-Alive to prevent NAT routers from timing out TCP
-// connections. See http://crbug.com/27400 for details.
-bool SetTCPKeepAlive(SOCKET socket, BOOL enable, int delay_secs) {
- int delay = delay_secs * 1000;
- struct tcp_keepalive keepalive_vals = {
- enable ? 1 : 0, // TCP keep-alive on.
- delay, // Delay seconds before sending first TCP keep-alive packet.
- delay, // Delay seconds between sending TCP keep-alive packets.
- };
- DWORD bytes_returned = 0xABAB;
- int rv = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &keepalive_vals,
- sizeof(keepalive_vals), NULL, 0,
- &bytes_returned, NULL, NULL);
- DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket
- << " [error: " << WSAGetLastError() << "].";
-
- // Disregard any failure in disabling nagle or enabling TCP Keep-Alive.
- return rv == 0;
-}
-
-// Sets socket parameters. Returns the OS error code (or 0 on
-// success).
-int SetupSocket(SOCKET socket) {
- // Increase the socket buffer sizes from the default sizes for WinXP. In
- // performance testing, there is substantial benefit by increasing from 8KB
- // to 64KB.
- // See also:
- // http://support.microsoft.com/kb/823764/EN-US
- // On Vista, if we manually set these sizes, Vista turns off its receive
- // window auto-tuning feature.
- // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx
- // Since Vista's auto-tune is better than any static value we can could set,
- // only change these on pre-vista machines.
- if (base::win::GetVersion() < base::win::VERSION_VISTA) {
- const int32 kSocketBufferSize = 64 * 1024;
- SetSocketReceiveBufferSize(socket, kSocketBufferSize);
- SetSocketSendBufferSize(socket, kSocketBufferSize);
- }
-
- DisableNagle(socket, true);
- SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds);
- return 0;
-}
-
-// Creates a new socket and sets default parameters for it. Returns
-// the OS error code (or 0 on success).
-int CreateSocket(int family, SOCKET* socket) {
- *socket = CreatePlatformSocket(family, SOCK_STREAM, IPPROTO_TCP);
- if (*socket == INVALID_SOCKET) {
- int os_error = WSAGetLastError();
- LOG(ERROR) << "CreatePlatformSocket failed: " << os_error;
- return os_error;
- }
- int error = SetupSocket(*socket);
- if (error) {
- if (closesocket(*socket) < 0)
- PLOG(ERROR) << "closesocket";
- *socket = INVALID_SOCKET;
- return error;
- }
- return 0;
-}
-
-int MapConnectError(int os_error) {
- switch (os_error) {
- // connect fails with WSAEACCES when Windows Firewall blocks the
- // connection.
- case WSAEACCES:
- return ERR_NETWORK_ACCESS_DENIED;
- case WSAETIMEDOUT:
- return ERR_CONNECTION_TIMED_OUT;
- default: {
- int net_error = MapSystemError(os_error);
- if (net_error == ERR_FAILED)
- return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
-
- // Give a more specific error when the user is offline.
- if (net_error == ERR_ADDRESS_UNREACHABLE &&
- NetworkChangeNotifier::IsOffline()) {
- return ERR_INTERNET_DISCONNECTED;
- }
-
- return net_error;
- }
- }
-}
-
-} // namespace
-
-//-----------------------------------------------------------------------------
-
-// This class encapsulates all the state that has to be preserved as long as
-// there is a network IO operation in progress. If the owner TCPClientSocketWin
-// is destroyed while an operation is in progress, the Core is detached and it
-// lives until the operation completes and the OS doesn't reference any resource
-// declared on this class anymore.
-class TCPClientSocketWin::Core : public base::RefCounted<Core> {
- public:
- explicit Core(TCPClientSocketWin* socket);
-
- // Start watching for the end of a read or write operation.
- void WatchForRead();
- void WatchForWrite();
-
- // The TCPClientSocketWin is going away.
- void Detach() { socket_ = NULL; }
-
- // The separate OVERLAPPED variables for asynchronous operation.
- // |read_overlapped_| is used for both Connect() and Read().
- // |write_overlapped_| is only used for Write();
- OVERLAPPED read_overlapped_;
- OVERLAPPED write_overlapped_;
-
- // The buffers used in Read() and Write().
- scoped_refptr<IOBuffer> read_iobuffer_;
- scoped_refptr<IOBuffer> write_iobuffer_;
- int read_buffer_length_;
- int write_buffer_length_;
-
- bool non_blocking_reads_initialized_;
-
- private:
- friend class base::RefCounted<Core>;
-
- class ReadDelegate : public base::win::ObjectWatcher::Delegate {
- public:
- explicit ReadDelegate(Core* core) : core_(core) {}
- virtual ~ReadDelegate() {}
-
- // base::ObjectWatcher::Delegate methods:
- virtual void OnObjectSignaled(HANDLE object);
-
- private:
- Core* const core_;
- };
-
- class WriteDelegate : public base::win::ObjectWatcher::Delegate {
- public:
- explicit WriteDelegate(Core* core) : core_(core) {}
- virtual ~WriteDelegate() {}
-
- // base::ObjectWatcher::Delegate methods:
- virtual void OnObjectSignaled(HANDLE object);
-
- private:
- Core* const core_;
- };
-
- ~Core();
-
- // The socket that created this object.
- TCPClientSocketWin* socket_;
-
- // |reader_| handles the signals from |read_watcher_|.
- ReadDelegate reader_;
- // |writer_| handles the signals from |write_watcher_|.
- WriteDelegate writer_;
-
- // |read_watcher_| watches for events from Connect() and Read().
- base::win::ObjectWatcher read_watcher_;
- // |write_watcher_| watches for events from Write();
- base::win::ObjectWatcher write_watcher_;
-
- DISALLOW_COPY_AND_ASSIGN(Core);
-};
-
-TCPClientSocketWin::Core::Core(
- TCPClientSocketWin* socket)
- : read_buffer_length_(0),
- write_buffer_length_(0),
- non_blocking_reads_initialized_(false),
- socket_(socket),
- reader_(this),
- writer_(this) {
- memset(&read_overlapped_, 0, sizeof(read_overlapped_));
- memset(&write_overlapped_, 0, sizeof(write_overlapped_));
-
- read_overlapped_.hEvent = WSACreateEvent();
- write_overlapped_.hEvent = WSACreateEvent();
-}
-
-TCPClientSocketWin::Core::~Core() {
- // Make sure the message loop is not watching this object anymore.
- read_watcher_.StopWatching();
- write_watcher_.StopWatching();
-
- WSACloseEvent(read_overlapped_.hEvent);
- memset(&read_overlapped_, 0xaf, sizeof(read_overlapped_));
- WSACloseEvent(write_overlapped_.hEvent);
- memset(&write_overlapped_, 0xaf, sizeof(write_overlapped_));
-}
-
-void TCPClientSocketWin::Core::WatchForRead() {
- // We grab an extra reference because there is an IO operation in progress.
- // Balanced in ReadDelegate::OnObjectSignaled().
- AddRef();
- read_watcher_.StartWatching(read_overlapped_.hEvent, &reader_);
-}
-
-void TCPClientSocketWin::Core::WatchForWrite() {
- // We grab an extra reference because there is an IO operation in progress.
- // Balanced in WriteDelegate::OnObjectSignaled().
- AddRef();
- write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_);
-}
-
-void TCPClientSocketWin::Core::ReadDelegate::OnObjectSignaled(
- HANDLE object) {
- DCHECK_EQ(object, core_->read_overlapped_.hEvent);
- if (core_->socket_) {
- if (core_->socket_->waiting_connect())
- core_->socket_->DidCompleteConnect();
- else
- core_->socket_->DidSignalRead();
- }
-
- core_->Release();
-}
-
-void TCPClientSocketWin::Core::WriteDelegate::OnObjectSignaled(
- HANDLE object) {
- DCHECK_EQ(object, core_->write_overlapped_.hEvent);
- if (core_->socket_)
- core_->socket_->DidCompleteWrite();
-
- core_->Release();
-}
-
-//-----------------------------------------------------------------------------
-
-TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses,
- net::NetLog* net_log,
- const net::NetLog::Source& source)
- : socket_(INVALID_SOCKET),
- bound_socket_(INVALID_SOCKET),
- addresses_(addresses),
- current_address_index_(-1),
- waiting_read_(false),
- waiting_write_(false),
- next_connect_state_(CONNECT_STATE_NONE),
- connect_os_error_(0),
- net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)),
- previously_disconnected_(false) {
- net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
- source.ToEventParametersCallback());
- EnsureWinsockInit();
-}
-
-TCPClientSocketWin::~TCPClientSocketWin() {
- Disconnect();
- net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
-}
-
-int TCPClientSocketWin::AdoptSocket(SOCKET socket) {
- DCHECK_EQ(socket_, INVALID_SOCKET);
-
- int error = SetupSocket(socket);
- if (error)
- return MapSystemError(error);
-
- socket_ = socket;
- SetNonBlocking(socket_);
-
- core_ = new Core(this);
- current_address_index_ = 0;
- use_history_.set_was_ever_connected();
-
- return OK;
-}
-
-int TCPClientSocketWin::Bind(const IPEndPoint& address) {
- if (current_address_index_ >= 0 || bind_address_.get()) {
- // Cannot bind the socket if we are already connected or connecting.
- return ERR_UNEXPECTED;
- }
-
- SockaddrStorage storage;
- if (!address.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
-
- // Create |bound_socket_| and try to bind it to |address|.
- int error = CreateSocket(address.GetSockAddrFamily(), &bound_socket_);
- if (error)
- return MapSystemError(error);
-
- if (bind(bound_socket_, storage.addr, storage.addr_len)) {
- error = errno;
- if (closesocket(bound_socket_) < 0)
- PLOG(ERROR) << "closesocket";
- bound_socket_ = INVALID_SOCKET;
- return MapSystemError(error);
- }
-
- bind_address_.reset(new IPEndPoint(address));
-
- return 0;
-}
-
-
-int TCPClientSocketWin::Connect(const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
-
- // If already connected, then just return OK.
- if (socket_ != INVALID_SOCKET)
- return OK;
-
- base::StatsCounter connects("tcp.connect");
- connects.Increment();
-
- net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
- addresses_.CreateNetLogCallback());
-
- // We will try to connect to each address in addresses_. Start with the
- // first one in the list.
- next_connect_state_ = CONNECT_STATE_CONNECT;
- current_address_index_ = 0;
-
- int rv = DoConnectLoop(OK);
- if (rv == ERR_IO_PENDING) {
- // Synchronous operation not supported.
- DCHECK(!callback.is_null());
- // TODO(ajwong): Is setting read_callback_ the right thing to do here??
- read_callback_ = callback;
- } else {
- LogConnectCompletion(rv);
- }
-
- return rv;
-}
-
-int TCPClientSocketWin::DoConnectLoop(int result) {
- DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
-
- int rv = result;
- do {
- ConnectState state = next_connect_state_;
- next_connect_state_ = CONNECT_STATE_NONE;
- switch (state) {
- case CONNECT_STATE_CONNECT:
- DCHECK_EQ(OK, rv);
- rv = DoConnect();
- break;
- case CONNECT_STATE_CONNECT_COMPLETE:
- rv = DoConnectComplete(rv);
- break;
- default:
- LOG(DFATAL) << "bad state " << state;
- rv = ERR_UNEXPECTED;
- break;
- }
- } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
-
- return rv;
-}
-
-int TCPClientSocketWin::DoConnect() {
- DCHECK_GE(current_address_index_, 0);
- DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size()));
- DCHECK_EQ(0, connect_os_error_);
-
- const IPEndPoint& endpoint = addresses_[current_address_index_];
-
- if (previously_disconnected_) {
- use_history_.Reset();
- previously_disconnected_ = false;
- }
-
- net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
- CreateNetLogIPEndPointCallback(&endpoint));
-
- next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
-
- if (bound_socket_ != INVALID_SOCKET) {
- DCHECK(bind_address_.get());
- socket_ = bound_socket_;
- bound_socket_ = INVALID_SOCKET;
- } else {
- connect_os_error_ = CreateSocket(endpoint.GetSockAddrFamily(), &socket_);
- if (connect_os_error_ != 0)
- return MapSystemError(connect_os_error_);
-
- if (bind_address_.get()) {
- SockaddrStorage storage;
- if (!bind_address_->ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
- if (bind(socket_, storage.addr, storage.addr_len))
- return MapSystemError(errno);
- }
- }
-
- DCHECK(!core_);
- core_ = new Core(this);
- // WSAEventSelect sets the socket to non-blocking mode as a side effect.
- // Our connect() and recv() calls require that the socket be non-blocking.
- WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT);
-
- SockaddrStorage storage;
- if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len))
- return ERR_INVALID_ARGUMENT;
- if (!connect(socket_, storage.addr, storage.addr_len)) {
- // Connected without waiting!
- //
- // The MSDN page for connect says:
- // With a nonblocking socket, the connection attempt cannot be completed
- // immediately. In this case, connect will return SOCKET_ERROR, and
- // WSAGetLastError will return WSAEWOULDBLOCK.
- // which implies that for a nonblocking socket, connect never returns 0.
- // It's not documented whether the event object will be signaled or not
- // if connect does return 0. So the code below is essentially dead code
- // and we don't know if it's correct.
- NOTREACHED();
-
- if (ResetEventIfSignaled(core_->read_overlapped_.hEvent))
- return OK;
- } else {
- int os_error = WSAGetLastError();
- if (os_error != WSAEWOULDBLOCK) {
- LOG(ERROR) << "connect failed: " << os_error;
- connect_os_error_ = os_error;
- return MapConnectError(os_error);
- }
- }
-
- core_->WatchForRead();
- return ERR_IO_PENDING;
-}
-
-int TCPClientSocketWin::DoConnectComplete(int result) {
- // Log the end of this attempt (and any OS error it threw).
- int os_error = connect_os_error_;
- connect_os_error_ = 0;
- if (result != OK) {
- net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
- NetLog::IntegerCallback("os_error", os_error));
- } else {
- net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT);
- }
-
- if (result == OK) {
- use_history_.set_was_ever_connected();
- return OK; // Done!
- }
-
- // Close whatever partially connected socket we currently have.
- DoDisconnect();
-
- // Try to fall back to the next address in the list.
- if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) {
- next_connect_state_ = CONNECT_STATE_CONNECT;
- ++current_address_index_;
- return OK;
- }
-
- // Otherwise there is nothing to fall back to, so give up.
- return result;
-}
-
-void TCPClientSocketWin::Disconnect() {
- DCHECK(CalledOnValidThread());
-
- DoDisconnect();
- current_address_index_ = -1;
- bind_address_.reset();
-}
-
-void TCPClientSocketWin::DoDisconnect() {
- DCHECK(CalledOnValidThread());
-
- if (socket_ == INVALID_SOCKET)
- return;
-
- // Note: don't use CancelIo to cancel pending IO because it doesn't work
- // when there is a Winsock layered service provider.
-
- // In most socket implementations, closing a socket results in a graceful
- // connection shutdown, but in Winsock we have to call shutdown explicitly.
- // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
- // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
- shutdown(socket_, SD_SEND);
-
- // This cancels any pending IO.
- closesocket(socket_);
- socket_ = INVALID_SOCKET;
-
- if (waiting_connect()) {
- // We closed the socket, so this notification will never come.
- // From MSDN' WSAEventSelect documentation:
- // "Closing a socket with closesocket also cancels the association and
- // selection of network events specified in WSAEventSelect for the socket".
- core_->Release();
- }
-
- waiting_read_ = false;
- waiting_write_ = false;
-
- core_->Detach();
- core_ = NULL;
-
- previously_disconnected_ = true;
-}
-
-bool TCPClientSocketWin::IsConnected() const {
- DCHECK(CalledOnValidThread());
-
- if (socket_ == INVALID_SOCKET || waiting_connect())
- return false;
-
- if (waiting_read_)
- return true;
-
- // Check if connection is alive.
- char c;
- int rv = recv(socket_, &c, 1, MSG_PEEK);
- if (rv == 0)
- return false;
- if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
- return false;
-
- return true;
-}
-
-bool TCPClientSocketWin::IsConnectedAndIdle() const {
- DCHECK(CalledOnValidThread());
-
- if (socket_ == INVALID_SOCKET || waiting_connect())
- return false;
-
- if (waiting_read_)
- return true;
-
- // Check if connection is alive and we haven't received any data
- // unexpectedly.
- char c;
- int rv = recv(socket_, &c, 1, MSG_PEEK);
- if (rv >= 0)
- return false;
- if (WSAGetLastError() != WSAEWOULDBLOCK)
- return false;
-
- return true;
-}
-
-int TCPClientSocketWin::GetPeerAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
- if (!IsConnected())
- return ERR_SOCKET_NOT_CONNECTED;
- *address = addresses_[current_address_index_];
- return OK;
-}
-
-int TCPClientSocketWin::GetLocalAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
- if (socket_ == INVALID_SOCKET) {
- if (bind_address_.get()) {
- *address = *bind_address_;
- return OK;
- }
- return ERR_SOCKET_NOT_CONNECTED;
- }
-
- struct sockaddr_storage addr_storage;
- socklen_t addr_len = sizeof(addr_storage);
- struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage);
- if (getsockname(socket_, addr, &addr_len))
- return MapSystemError(WSAGetLastError());
- if (!address->FromSockAddr(addr, addr_len))
- return ERR_FAILED;
- return OK;
-}
-
-void TCPClientSocketWin::SetSubresourceSpeculation() {
- use_history_.set_subresource_speculation();
-}
-
-void TCPClientSocketWin::SetOmniboxSpeculation() {
- use_history_.set_omnibox_speculation();
-}
-
-bool TCPClientSocketWin::WasEverUsed() const {
- return use_history_.was_used_to_convey_data();
-}
-
-bool TCPClientSocketWin::UsingTCPFastOpen() const {
- // Not supported on windows.
- return false;
-}
-
-bool TCPClientSocketWin::WasNpnNegotiated() const {
- return false;
-}
-
-NextProto TCPClientSocketWin::GetNegotiatedProtocol() const {
- return kProtoUnknown;
-}
-
-bool TCPClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) {
- return false;
-}
-
-int TCPClientSocketWin::Read(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK_NE(socket_, INVALID_SOCKET);
- DCHECK(!waiting_read_);
- DCHECK(read_callback_.is_null());
- DCHECK(!core_->read_iobuffer_);
-
- return DoRead(buf, buf_len, callback);
-}
-
-int TCPClientSocketWin::Write(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK_NE(socket_, INVALID_SOCKET);
- DCHECK(!waiting_write_);
- DCHECK(write_callback_.is_null());
- DCHECK_GT(buf_len, 0);
- DCHECK(!core_->write_iobuffer_);
-
- base::StatsCounter writes("tcp.writes");
- writes.Increment();
-
- WSABUF write_buffer;
- write_buffer.len = buf_len;
- write_buffer.buf = buf->data();
-
- // TODO(wtc): Remove the assertion after enough testing.
- AssertEventNotSignaled(core_->write_overlapped_.hEvent);
- DWORD num;
- int rv = WSASend(socket_, &write_buffer, 1, &num, 0,
- &core_->write_overlapped_, NULL);
- if (rv == 0) {
- if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) {
- rv = static_cast<int>(num);
- if (rv > buf_len || rv < 0) {
- // It seems that some winsock interceptors report that more was written
- // than was available. Treat this as an error. http://crbug.com/27870
- LOG(ERROR) << "Detected broken LSP: Asked to write " << buf_len
- << " bytes, but " << rv << " bytes reported.";
- return ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
- }
- base::StatsCounter write_bytes("tcp.write_bytes");
- write_bytes.Add(rv);
- if (rv > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv,
- buf->data());
- return rv;
- }
- } else {
- int os_error = WSAGetLastError();
- if (os_error != WSA_IO_PENDING) {
- int net_error = MapSystemError(os_error);
- net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
- CreateNetLogSocketErrorCallback(net_error, os_error));
- return net_error;
- }
- }
- waiting_write_ = true;
- write_callback_ = callback;
- core_->write_iobuffer_ = buf;
- core_->write_buffer_length_ = buf_len;
- core_->WatchForWrite();
- return ERR_IO_PENDING;
-}
-
-bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) {
- DCHECK(CalledOnValidThread());
- return SetSocketReceiveBufferSize(socket_, size);
-}
-
-bool TCPClientSocketWin::SetSendBufferSize(int32 size) {
- DCHECK(CalledOnValidThread());
- return SetSocketSendBufferSize(socket_, size);
-}
-
-bool TCPClientSocketWin::SetKeepAlive(bool enable, int delay) {
- return SetTCPKeepAlive(socket_, enable, delay);
-}
-
-bool TCPClientSocketWin::SetNoDelay(bool no_delay) {
- return DisableNagle(socket_, no_delay);
-}
-
-void TCPClientSocketWin::LogConnectCompletion(int net_error) {
- if (net_error == OK)
- UpdateConnectionTypeHistograms(CONNECTION_ANY);
-
- if (net_error != OK) {
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error);
- return;
- }
-
- struct sockaddr_storage source_address;
- socklen_t addrlen = sizeof(source_address);
- int rv = getsockname(
- socket_, reinterpret_cast<struct sockaddr*>(&source_address), &addrlen);
- if (rv != 0) {
- LOG(ERROR) << "getsockname() [rv: " << rv
- << "] error: " << WSAGetLastError();
- NOTREACHED();
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv);
- return;
- }
-
- net_log_.EndEvent(
- NetLog::TYPE_TCP_CONNECT,
- CreateNetLogSourceAddressCallback(
- reinterpret_cast<const struct sockaddr*>(&source_address),
- sizeof(source_address)));
-}
-
-int TCPClientSocketWin::DoRead(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback) {
- if (!core_->non_blocking_reads_initialized_) {
- WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
- FD_READ | FD_CLOSE);
- core_->non_blocking_reads_initialized_ = true;
- }
- int rv = recv(socket_, buf->data(), buf_len, 0);
- if (rv == SOCKET_ERROR) {
- int os_error = WSAGetLastError();
- if (os_error != WSAEWOULDBLOCK) {
- int net_error = MapSystemError(os_error);
- net_log_.AddEvent(
- NetLog::TYPE_SOCKET_READ_ERROR,
- CreateNetLogSocketErrorCallback(net_error, os_error));
- return net_error;
- }
- } else {
- base::StatsCounter read_bytes("tcp.read_bytes");
- if (rv > 0) {
- use_history_.set_was_used_to_convey_data();
- read_bytes.Add(rv);
- }
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
- buf->data());
- return rv;
- }
-
- waiting_read_ = true;
- read_callback_ = callback;
- core_->read_iobuffer_ = buf;
- core_->read_buffer_length_ = buf_len;
- core_->WatchForRead();
- return ERR_IO_PENDING;
-}
-
-void TCPClientSocketWin::DoReadCallback(int rv) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(!read_callback_.is_null());
-
- // Since Run may result in Read being called, clear read_callback_ up front.
- CompletionCallback c = read_callback_;
- read_callback_.Reset();
- c.Run(rv);
-}
-
-void TCPClientSocketWin::DoWriteCallback(int rv) {
- DCHECK_NE(rv, ERR_IO_PENDING);
- DCHECK(!write_callback_.is_null());
-
- // Since Run may result in Write being called, clear write_callback_ up front.
- CompletionCallback c = write_callback_;
- write_callback_.Reset();
- c.Run(rv);
-}
-
-void TCPClientSocketWin::DidCompleteConnect() {
- DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
- int result;
-
- WSANETWORKEVENTS events;
- int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
- &events);
- int os_error = 0;
- if (rv == SOCKET_ERROR) {
- NOTREACHED();
- os_error = WSAGetLastError();
- result = MapSystemError(os_error);
- } else if (events.lNetworkEvents & FD_CONNECT) {
- os_error = events.iErrorCode[FD_CONNECT_BIT];
- result = MapConnectError(os_error);
- } else {
- NOTREACHED();
- result = ERR_UNEXPECTED;
- }
-
- connect_os_error_ = os_error;
- rv = DoConnectLoop(result);
- if (rv != ERR_IO_PENDING) {
- LogConnectCompletion(rv);
- DoReadCallback(rv);
- }
-}
-
-void TCPClientSocketWin::DidCompleteWrite() {
- DCHECK(waiting_write_);
-
- DWORD num_bytes, flags;
- BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_,
- &num_bytes, FALSE, &flags);
- WSAResetEvent(core_->write_overlapped_.hEvent);
- waiting_write_ = false;
- int rv;
- if (!ok) {
- int os_error = WSAGetLastError();
- rv = MapSystemError(os_error);
- net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
- CreateNetLogSocketErrorCallback(rv, os_error));
- } else {
- rv = static_cast<int>(num_bytes);
- if (rv > core_->write_buffer_length_ || rv < 0) {
- // It seems that some winsock interceptors report that more was written
- // than was available. Treat this as an error. http://crbug.com/27870
- LOG(ERROR) << "Detected broken LSP: Asked to write "
- << core_->write_buffer_length_ << " bytes, but " << rv
- << " bytes reported.";
- rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
- } else {
- base::StatsCounter write_bytes("tcp.write_bytes");
- write_bytes.Add(num_bytes);
- if (num_bytes > 0)
- use_history_.set_was_used_to_convey_data();
- net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes,
- core_->write_iobuffer_->data());
- }
- }
- core_->write_iobuffer_ = NULL;
- DoWriteCallback(rv);
-}
-
-void TCPClientSocketWin::DidSignalRead() {
- DCHECK(waiting_read_);
- int os_error = 0;
- WSANETWORKEVENTS network_events;
- int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
- &network_events);
- if (rv == SOCKET_ERROR) {
- os_error = WSAGetLastError();
- rv = MapSystemError(os_error);
- } else if (network_events.lNetworkEvents) {
- DCHECK_EQ(network_events.lNetworkEvents & ~(FD_READ | FD_CLOSE), 0);
- // If network_events.lNetworkEvents is FD_CLOSE and
- // network_events.iErrorCode[FD_CLOSE_BIT] is 0, it is a graceful
- // connection closure. It is tempting to directly set rv to 0 in
- // this case, but the MSDN pages for WSAEventSelect and
- // WSAAsyncSelect recommend we still call DoRead():
- // FD_CLOSE should only be posted after all data is read from a
- // socket, but an application should check for remaining data upon
- // receipt of FD_CLOSE to avoid any possibility of losing data.
- //
- // If network_events.iErrorCode[FD_READ_BIT] or
- // network_events.iErrorCode[FD_CLOSE_BIT] is nonzero, still call
- // DoRead() because recv() reports a more accurate error code
- // (WSAECONNRESET vs. WSAECONNABORTED) when the connection was
- // reset.
- rv = DoRead(core_->read_iobuffer_, core_->read_buffer_length_,
- read_callback_);
- if (rv == ERR_IO_PENDING)
- return;
- } else {
- // This may happen because Read() may succeed synchronously and
- // consume all the received data without resetting the event object.
- core_->WatchForRead();
- return;
- }
- waiting_read_ = false;
- core_->read_iobuffer_ = NULL;
- core_->read_buffer_length_ = 0;
- DoReadCallback(rv);
-}
-
-} // namespace net
diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h
deleted file mode 100644
index c899f27e70..0000000000
--- a/net/socket/tcp_client_socket_win.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_
-#define NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_
-
-#include <winsock2.h>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "net/base/address_list.h"
-#include "net/base/completion_callback.h"
-#include "net/base/net_log.h"
-#include "net/socket/stream_socket.h"
-
-namespace net {
-
-class BoundNetLog;
-
-class NET_EXPORT TCPClientSocketWin : public StreamSocket,
- NON_EXPORTED_BASE(base::NonThreadSafe) {
- public:
- // The IP address(es) and port number to connect to. The TCP socket will try
- // each IP address in the list until it succeeds in establishing a
- // connection.
- TCPClientSocketWin(const AddressList& addresses,
- net::NetLog* net_log,
- const net::NetLog::Source& source);
-
- virtual ~TCPClientSocketWin();
-
- // AdoptSocket causes the given, connected socket to be adopted as a TCP
- // socket. This object must not be connected. This object takes ownership of
- // the given socket and then acts as if Connect() had been called. This
- // function is used by TCPServerSocket() to adopt accepted connections
- // and for testing.
- int AdoptSocket(SOCKET socket);
-
- // Binds the socket to a local IP address and port.
- int Bind(const IPEndPoint& address);
-
- // StreamSocket implementation.
- virtual int Connect(const CompletionCallback& callback);
- virtual void Disconnect();
- virtual bool IsConnected() const;
- virtual bool IsConnectedAndIdle() const;
- virtual int GetPeerAddress(IPEndPoint* address) const;
- virtual int GetLocalAddress(IPEndPoint* address) const;
- virtual const BoundNetLog& NetLog() const { return net_log_; }
- virtual void SetSubresourceSpeculation();
- virtual void SetOmniboxSpeculation();
- virtual bool WasEverUsed() const;
- virtual bool UsingTCPFastOpen() const;
- virtual bool WasNpnNegotiated() const OVERRIDE;
- virtual NextProto GetNegotiatedProtocol() const OVERRIDE;
- virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
-
- // Socket implementation.
- // Multiple outstanding requests are not supported.
- // Full duplex mode (reading and writing at the same time) is supported
- virtual int Read(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
- virtual int Write(IOBuffer* buf, int buf_len,
- const CompletionCallback& callback);
-
- virtual bool SetReceiveBufferSize(int32 size);
- virtual bool SetSendBufferSize(int32 size);
-
- virtual bool SetKeepAlive(bool enable, int delay);
- virtual bool SetNoDelay(bool no_delay);
-
- private:
- // State machine for connecting the socket.
- enum ConnectState {
- CONNECT_STATE_CONNECT,
- CONNECT_STATE_CONNECT_COMPLETE,
- CONNECT_STATE_NONE,
- };
-
- class Core;
-
- // State machine used by Connect().
- int DoConnectLoop(int result);
- int DoConnect();
- int DoConnectComplete(int result);
-
- // Helper used by Disconnect(), which disconnects minus the logging and
- // resetting of current_address_index_.
- void DoDisconnect();
-
- // Returns true if a Connect() is in progress.
- bool waiting_connect() const {
- return next_connect_state_ != CONNECT_STATE_NONE;
- }
-
- // Called after Connect() has completed with |net_error|.
- void LogConnectCompletion(int net_error);
-
- int DoRead(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
- void DoReadCallback(int rv);
- void DoWriteCallback(int rv);
- void DidCompleteConnect();
- void DidCompleteWrite();
- void DidSignalRead();
-
- SOCKET socket_;
-
- // Local IP address and port we are bound to. Set to NULL if Bind()
- // was't called (in that cases OS chooses address/port).
- scoped_ptr<IPEndPoint> bind_address_;
-
- // Stores bound socket between Bind() and Connect() calls.
- SOCKET bound_socket_;
-
- // The list of addresses we should try in order to establish a connection.
- AddressList addresses_;
-
- // Where we are in above list. Set to -1 if uninitialized.
- int current_address_index_;
-
- // The various states that the socket could be in.
- bool waiting_read_;
- bool waiting_write_;
-
- // The core of the socket that can live longer than the socket itself. We pass
- // resources to the Windows async IO functions and we have to make sure that
- // they are not destroyed while the OS still references them.
- scoped_refptr<Core> core_;
-
- // External callback; called when connect or read is complete.
- CompletionCallback read_callback_;
-
- // External callback; called when write is complete.
- CompletionCallback write_callback_;
-
- // The next state for the Connect() state machine.
- ConnectState next_connect_state_;
-
- // The OS error that CONNECT_STATE_CONNECT last completed with.
- int connect_os_error_;
-
- BoundNetLog net_log_;
-
- // This socket was previously disconnected and has not been re-connected.
- bool previously_disconnected_;
-
- // Record of connectivity and transmissions, for use in speculative connection
- // histograms.
- UseHistory use_history_;
-
- DISALLOW_COPY_AND_ASSIGN(TCPClientSocketWin);
-};
-
-} // namespace net
-
-#endif // NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_
diff --git a/net/socket/tcp_server_socket.cc b/net/socket/tcp_server_socket.cc
index 13e9de1e59..a25f73f6c6 100644
--- a/net/socket/tcp_server_socket.cc
+++ b/net/socket/tcp_server_socket.cc
@@ -7,7 +7,6 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
-#include "build/build_config.h"
#include "net/base/net_errors.h"
#include "net/socket/tcp_client_socket.h"
@@ -22,7 +21,7 @@ TCPServerSocket::~TCPServerSocket() {
}
int TCPServerSocket::Listen(const IPEndPoint& address, int backlog) {
- int result = socket_.Create(address.GetFamily());
+ int result = socket_.Open(address.GetFamily());
if (result != OK)
return result;
@@ -88,26 +87,9 @@ int TCPServerSocket::ConvertAcceptedSocket(
if (result != OK)
return result;
- scoped_ptr<TCPClientSocket> client_socket(new TCPClientSocket(
- AddressList(accepted_address_),
- temp_accepted_socket->net_log().net_log(),
- temp_accepted_socket->net_log().source()));
- // TODO(yzshen): Once we switch TCPClientSocket::AdoptSocket() to take a
- // TCPSocket object, we don't need to do platform-specific handling.
-#if defined(OS_WIN)
- SOCKET raw_socket = temp_accepted_socket->Release();
-#elif defined(OS_POSIX)
- int raw_socket = temp_accepted_socket->Release();
-#endif
- result = client_socket->AdoptSocket(raw_socket);
- if (result != OK) {
- // |client_socket| won't take ownership of |raw_socket| on failure.
- // Therefore, we put it back into |temp_accepted_socket| to close it.
- temp_accepted_socket->Adopt(raw_socket);
- return result;
- }
+ output_accepted_socket->reset(new TCPClientSocket(
+ temp_accepted_socket.Pass(), accepted_address_));
- *output_accepted_socket = client_socket.Pass();
return OK;
}
diff --git a/net/socket/tcp_socket.cc b/net/socket/tcp_socket.cc
new file mode 100644
index 0000000000..fd72f6b464
--- /dev/null
+++ b/net/socket/tcp_socket.cc
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/socket/tcp_socket.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+
+namespace net {
+
+namespace {
+
+#if defined(OS_LINUX)
+
+// Checks to see if the system supports TCP FastOpen. Notably, it requires
+// kernel support. Additionally, this checks system configuration to ensure that
+// it's enabled.
+bool SystemSupportsTCPFastOpen() {
+ static const base::FilePath::CharType kTCPFastOpenProcFilePath[] =
+ "/proc/sys/net/ipv4/tcp_fastopen";
+ std::string system_enabled_tcp_fastopen;
+ if (!base::ReadFileToString(
+ base::FilePath(kTCPFastOpenProcFilePath),
+ &system_enabled_tcp_fastopen)) {
+ return false;
+ }
+
+ // As per http://lxr.linux.no/linux+v3.7.7/include/net/tcp.h#L225
+ // TFO_CLIENT_ENABLE is the LSB
+ if (system_enabled_tcp_fastopen.empty() ||
+ (system_enabled_tcp_fastopen[0] & 0x1) == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+#else
+
+bool SystemSupportsTCPFastOpen() {
+ return false;
+}
+
+#endif
+
+bool g_tcp_fastopen_enabled = false;
+
+} // namespace
+
+void SetTCPFastOpenEnabled(bool value) {
+ g_tcp_fastopen_enabled = value && SystemSupportsTCPFastOpen();
+}
+
+bool IsTCPFastOpenEnabled() {
+ return g_tcp_fastopen_enabled;
+}
+
+} // namespace net
diff --git a/net/socket/tcp_socket.h b/net/socket/tcp_socket.h
index aea8f12d65..8b36fade75 100644
--- a/net/socket/tcp_socket.h
+++ b/net/socket/tcp_socket.h
@@ -6,6 +6,7 @@
#define NET_SOCKET_TCP_SOCKET_H_
#include "build/build_config.h"
+#include "net/base/net_export.h"
#if defined(OS_WIN)
#include "net/socket/tcp_socket_win.h"
@@ -15,6 +16,13 @@
namespace net {
+// Enable/disable experimental TCP FastOpen option.
+// Not thread safe. Must be called during initialization/startup only.
+NET_EXPORT void SetTCPFastOpenEnabled(bool value);
+
+// Check if the TCP FastOpen option is enabled.
+bool IsTCPFastOpenEnabled();
+
// TCPSocket provides a platform-independent interface for TCP sockets.
//
// It is recommended to use TCPClientSocket/TCPServerSocket instead of this
diff --git a/net/socket/tcp_socket_libevent.cc b/net/socket/tcp_socket_libevent.cc
index 0bceaa49a8..66416f7020 100644
--- a/net/socket/tcp_socket_libevent.cc
+++ b/net/socket/tcp_socket_libevent.cc
@@ -2,46 +2,159 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/socket/tcp_socket_libevent.h"
+#include "net/socket/tcp_socket.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
-#include <sys/socket.h>
-
-#include "build/build_config.h"
-
-#if defined(OS_POSIX)
#include <netinet/in.h>
-#endif
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include "base/callback_helpers.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/stats_counters.h"
#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "net/base/address_list.h"
+#include "net/base/connection_type_histograms.h"
+#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
-#include "net/socket/socket_descriptor.h"
+#include "net/base/network_change_notifier.h"
#include "net/socket/socket_net_log_params.h"
+// If we don't have a definition for TCPI_OPT_SYN_DATA, create one.
+#ifndef TCPI_OPT_SYN_DATA
+#define TCPI_OPT_SYN_DATA 32
+#endif
+
namespace net {
+namespace {
+
+const int kTCPKeepAliveSeconds = 45;
+
+// SetTCPNoDelay turns on/off buffering in the kernel. By default, TCP sockets
+// will wait up to 200ms for more data to complete a packet before transmitting.
+// After calling this function, the kernel will not wait. See TCP_NODELAY in
+// `man 7 tcp`.
+bool SetTCPNoDelay(int fd, bool no_delay) {
+ int on = no_delay ? 1 : 0;
+ int error = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ return error == 0;
+}
+
+// SetTCPKeepAlive sets SO_KEEPALIVE.
+bool SetTCPKeepAlive(int fd, bool enable, int delay) {
+ int on = enable ? 1 : 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) {
+ PLOG(ERROR) << "Failed to set SO_KEEPALIVE on fd: " << fd;
+ return false;
+ }
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ // Set seconds until first TCP keep alive.
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) {
+ PLOG(ERROR) << "Failed to set TCP_KEEPIDLE on fd: " << fd;
+ return false;
+ }
+ // Set seconds between TCP keep alives.
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &delay, sizeof(delay))) {
+ PLOG(ERROR) << "Failed to set TCP_KEEPINTVL on fd: " << fd;
+ return false;
+ }
+#endif
+ return true;
+}
+
+int MapConnectError(int os_error) {
+ switch (os_error) {
+ case EACCES:
+ return ERR_NETWORK_ACCESS_DENIED;
+ case ETIMEDOUT:
+ return ERR_CONNECTION_TIMED_OUT;
+ default: {
+ int net_error = MapSystemError(os_error);
+ if (net_error == ERR_FAILED)
+ return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
+
+ // Give a more specific error when the user is offline.
+ if (net_error == ERR_ADDRESS_UNREACHABLE &&
+ NetworkChangeNotifier::IsOffline()) {
+ return ERR_INTERNET_DISCONNECTED;
+ }
+ return net_error;
+ }
+ }
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+TCPSocketLibevent::Watcher::Watcher(
+ const base::Closure& read_ready_callback,
+ const base::Closure& write_ready_callback)
+ : read_ready_callback_(read_ready_callback),
+ write_ready_callback_(write_ready_callback) {
+}
+
+TCPSocketLibevent::Watcher::~Watcher() {
+}
+
+void TCPSocketLibevent::Watcher::OnFileCanReadWithoutBlocking(int /* fd */) {
+ if (!read_ready_callback_.is_null())
+ read_ready_callback_.Run();
+ else
+ NOTREACHED();
+}
+
+void TCPSocketLibevent::Watcher::OnFileCanWriteWithoutBlocking(int /* fd */) {
+ if (!write_ready_callback_.is_null())
+ write_ready_callback_.Run();
+ else
+ NOTREACHED();
+}
+
TCPSocketLibevent::TCPSocketLibevent(NetLog* net_log,
const NetLog::Source& source)
: socket_(kInvalidSocket),
+ accept_watcher_(base::Bind(&TCPSocketLibevent::DidCompleteAccept,
+ base::Unretained(this)),
+ base::Closure()),
accept_socket_(NULL),
accept_address_(NULL),
+ read_watcher_(base::Bind(&TCPSocketLibevent::DidCompleteRead,
+ base::Unretained(this)),
+ base::Closure()),
+ write_watcher_(base::Closure(),
+ base::Bind(&TCPSocketLibevent::DidCompleteConnectOrWrite,
+ base::Unretained(this))),
+ read_buf_len_(0),
+ write_buf_len_(0),
+ use_tcp_fastopen_(IsTCPFastOpenEnabled()),
+ tcp_fastopen_connected_(false),
+ fast_open_status_(FAST_OPEN_STATUS_UNKNOWN),
+ waiting_connect_(false),
+ connect_os_error_(0),
+ logging_multiple_connect_attempts_(false),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) {
net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
source.ToEventParametersCallback());
}
TCPSocketLibevent::~TCPSocketLibevent() {
- if (socket_ != kInvalidSocket)
- Close();
net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
+ if (tcp_fastopen_connected_) {
+ UMA_HISTOGRAM_ENUMERATION("Net.TcpFastOpenSocketConnection",
+ fast_open_status_, FAST_OPEN_MAX_VALUE);
+ }
+ Close();
}
-int TCPSocketLibevent::Create(AddressFamily family) {
+int TCPSocketLibevent::Open(AddressFamily family) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(socket_, kInvalidSocket);
@@ -61,7 +174,8 @@ int TCPSocketLibevent::Create(AddressFamily family) {
return OK;
}
-int TCPSocketLibevent::Adopt(int socket) {
+int TCPSocketLibevent::AdoptConnectedSocket(int socket,
+ const IPEndPoint& peer_address) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(socket_, kInvalidSocket);
@@ -73,16 +187,9 @@ int TCPSocketLibevent::Adopt(int socket) {
return result;
}
- return OK;
-}
-
-int TCPSocketLibevent::Release() {
- DCHECK(CalledOnValidThread());
- DCHECK(accept_callback_.is_null());
+ peer_address_.reset(new IPEndPoint(peer_address));
- int result = socket_;
- socket_ = kInvalidSocket;
- return result;
+ return OK;
}
int TCPSocketLibevent::Bind(const IPEndPoint& address) {
@@ -102,19 +209,6 @@ int TCPSocketLibevent::Bind(const IPEndPoint& address) {
return OK;
}
-int TCPSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
-
- SockaddrStorage storage;
- if (getsockname(socket_, storage.addr, &storage.addr_len) < 0)
- return MapSystemError(errno);
- if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
-
- return OK;
-}
-
int TCPSocketLibevent::Listen(int backlog) {
DCHECK(CalledOnValidThread());
DCHECK_GT(backlog, 0);
@@ -145,7 +239,7 @@ int TCPSocketLibevent::Accept(scoped_ptr<TCPSocketLibevent>* socket,
if (result == ERR_IO_PENDING) {
if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
socket_, true, base::MessageLoopForIO::WATCH_READ,
- &accept_socket_watcher_, this)) {
+ &accept_socket_watcher_, &accept_watcher_)) {
PLOG(ERROR) << "WatchFileDescriptor failed on read";
return MapSystemError(errno);
}
@@ -158,11 +252,201 @@ int TCPSocketLibevent::Accept(scoped_ptr<TCPSocketLibevent>* socket,
return result;
}
+int TCPSocketLibevent::Connect(const IPEndPoint& address,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(socket_, kInvalidSocket);
+ DCHECK(!waiting_connect_);
+
+ // |peer_address_| will be non-NULL if Connect() has been called. Unless
+ // Close() is called to reset the internal state, a second call to Connect()
+ // is not allowed.
+ // Please note that we don't allow a second Connect() even if the previous
+ // Connect() has failed. Connecting the same |socket_| again after a
+ // connection attempt failed results in unspecified behavior according to
+ // POSIX.
+ DCHECK(!peer_address_);
+
+ if (!logging_multiple_connect_attempts_)
+ LogConnectBegin(AddressList(address));
+
+ peer_address_.reset(new IPEndPoint(address));
+
+ int rv = DoConnect();
+ if (rv == ERR_IO_PENDING) {
+ // Synchronous operation not supported.
+ DCHECK(!callback.is_null());
+ write_callback_ = callback;
+ waiting_connect_ = true;
+ } else {
+ DoConnectComplete(rv);
+ }
+
+ return rv;
+}
+
+bool TCPSocketLibevent::IsConnected() const {
+ DCHECK(CalledOnValidThread());
+
+ if (socket_ == kInvalidSocket || waiting_connect_)
+ return false;
+
+ if (use_tcp_fastopen_ && !tcp_fastopen_connected_ && peer_address_) {
+ // With TCP FastOpen, we pretend that the socket is connected.
+ // This allows GetPeerAddress() to return peer_address_.
+ return true;
+ }
+
+ // Check if connection is alive.
+ char c;
+ int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
+ if (rv == 0)
+ return false;
+ if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+bool TCPSocketLibevent::IsConnectedAndIdle() const {
+ DCHECK(CalledOnValidThread());
+
+ if (socket_ == kInvalidSocket || waiting_connect_)
+ return false;
+
+ // TODO(wtc): should we also handle the TCP FastOpen case here,
+ // as we do in IsConnected()?
+
+ // Check if connection is alive and we haven't received any data
+ // unexpectedly.
+ char c;
+ int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
+ if (rv >= 0)
+ return false;
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+int TCPSocketLibevent::Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(kInvalidSocket, socket_);
+ DCHECK(!waiting_connect_);
+ DCHECK(read_callback_.is_null());
+ // Synchronous operation not supported
+ DCHECK(!callback.is_null());
+ DCHECK_GT(buf_len, 0);
+
+ int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
+ if (nread >= 0) {
+ base::StatsCounter read_bytes("tcp.read_bytes");
+ read_bytes.Add(nread);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, nread,
+ buf->data());
+ RecordFastOpenStatus();
+ return nread;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ int net_error = MapSystemError(errno);
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
+ CreateNetLogSocketErrorCallback(net_error, errno));
+ return net_error;
+ }
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, base::MessageLoopForIO::WATCH_READ,
+ &read_socket_watcher_, &read_watcher_)) {
+ DVLOG(1) << "WatchFileDescriptor failed on read, errno " << errno;
+ return MapSystemError(errno);
+ }
+
+ read_buf_ = buf;
+ read_buf_len_ = buf_len;
+ read_callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int TCPSocketLibevent::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(kInvalidSocket, socket_);
+ DCHECK(!waiting_connect_);
+ DCHECK(write_callback_.is_null());
+ // Synchronous operation not supported
+ DCHECK(!callback.is_null());
+ DCHECK_GT(buf_len, 0);
+
+ int nwrite = InternalWrite(buf, buf_len);
+ if (nwrite >= 0) {
+ base::StatsCounter write_bytes("tcp.write_bytes");
+ write_bytes.Add(nwrite);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, nwrite,
+ buf->data());
+ return nwrite;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ int net_error = MapSystemError(errno);
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
+ CreateNetLogSocketErrorCallback(net_error, errno));
+ return net_error;
+ }
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, base::MessageLoopForIO::WATCH_WRITE,
+ &write_socket_watcher_, &write_watcher_)) {
+ DVLOG(1) << "WatchFileDescriptor failed on write, errno " << errno;
+ return MapSystemError(errno);
+ }
+
+ write_buf_ = buf;
+ write_buf_len_ = buf_len;
+ write_callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int TCPSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(address);
+
+ SockaddrStorage storage;
+ if (getsockname(socket_, storage.addr, &storage.addr_len) < 0)
+ return MapSystemError(errno);
+ if (!address->FromSockAddr(storage.addr, storage.addr_len))
+ return ERR_ADDRESS_INVALID;
+
+ return OK;
+}
+
+int TCPSocketLibevent::GetPeerAddress(IPEndPoint* address) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(address);
+ if (!IsConnected())
+ return ERR_SOCKET_NOT_CONNECTED;
+ *address = *peer_address_;
+ return OK;
+}
+
int TCPSocketLibevent::SetDefaultOptionsForServer() {
+ DCHECK(CalledOnValidThread());
return SetAddressReuse(true);
}
+void TCPSocketLibevent::SetDefaultOptionsForClient() {
+ DCHECK(CalledOnValidThread());
+
+ // This mirrors the behaviour on Windows. See the comment in
+ // tcp_socket_win.cc after searching for "NODELAY".
+ SetTCPNoDelay(socket_, true); // If SetTCPNoDelay fails, we don't care.
+ SetTCPKeepAlive(socket_, true, kTCPKeepAliveSeconds);
+}
+
int TCPSocketLibevent::SetAddressReuse(bool allow) {
+ DCHECK(CalledOnValidThread());
+
// SO_REUSEADDR is useful for server sockets to bind to a recently unbound
// port. When a socket is closed, the end point changes its state to TIME_WAIT
// and wait for 2 MSL (maximum segment lifetime) to ensure the remote peer
@@ -184,14 +468,96 @@ int TCPSocketLibevent::SetAddressReuse(bool allow) {
return OK;
}
+bool TCPSocketLibevent::SetReceiveBufferSize(int32 size) {
+ DCHECK(CalledOnValidThread());
+ int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
+ reinterpret_cast<const char*>(&size),
+ sizeof(size));
+ DCHECK(!rv) << "Could not set socket receive buffer size: " << errno;
+ return rv == 0;
+}
+
+bool TCPSocketLibevent::SetSendBufferSize(int32 size) {
+ DCHECK(CalledOnValidThread());
+ int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<const char*>(&size),
+ sizeof(size));
+ DCHECK(!rv) << "Could not set socket send buffer size: " << errno;
+ return rv == 0;
+}
+
+bool TCPSocketLibevent::SetKeepAlive(bool enable, int delay) {
+ DCHECK(CalledOnValidThread());
+ return SetTCPKeepAlive(socket_, enable, delay);
+}
+
+bool TCPSocketLibevent::SetNoDelay(bool no_delay) {
+ DCHECK(CalledOnValidThread());
+ return SetTCPNoDelay(socket_, no_delay);
+}
+
void TCPSocketLibevent::Close() {
+ DCHECK(CalledOnValidThread());
+
+ bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ ok = read_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ ok = write_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+
if (socket_ != kInvalidSocket) {
- bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
- DCHECK(ok);
if (HANDLE_EINTR(close(socket_)) < 0)
PLOG(ERROR) << "close";
socket_ = kInvalidSocket;
}
+
+ if (!accept_callback_.is_null()) {
+ accept_socket_ = NULL;
+ accept_address_ = NULL;
+ accept_callback_.Reset();
+ }
+
+ if (!read_callback_.is_null()) {
+ read_buf_ = NULL;
+ read_buf_len_ = 0;
+ read_callback_.Reset();
+ }
+
+ if (!write_callback_.is_null()) {
+ write_buf_ = NULL;
+ write_buf_len_ = 0;
+ write_callback_.Reset();
+ }
+
+ tcp_fastopen_connected_ = false;
+ fast_open_status_ = FAST_OPEN_STATUS_UNKNOWN;
+ waiting_connect_ = false;
+ peer_address_.reset();
+ connect_os_error_ = 0;
+}
+
+bool TCPSocketLibevent::UsingTCPFastOpen() const {
+ return use_tcp_fastopen_;
+}
+
+void TCPSocketLibevent::StartLoggingMultipleConnectAttempts(
+ const AddressList& addresses) {
+ if (!logging_multiple_connect_attempts_) {
+ logging_multiple_connect_attempts_ = true;
+ LogConnectBegin(addresses);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void TCPSocketLibevent::EndLoggingMultipleConnectAttempts(int net_error) {
+ if (logging_multiple_connect_attempts_) {
+ LogConnectEnd(net_error);
+ logging_multiple_connect_attempts_ = false;
+ } else {
+ NOTREACHED();
+ }
}
int TCPSocketLibevent::AcceptInternal(scoped_ptr<TCPSocketLibevent>* socket,
@@ -212,12 +578,13 @@ int TCPSocketLibevent::AcceptInternal(scoped_ptr<TCPSocketLibevent>* socket,
NOTREACHED();
if (HANDLE_EINTR(close(new_socket)) < 0)
PLOG(ERROR) << "close";
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED);
- return ERR_FAILED;
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT,
+ ERR_ADDRESS_INVALID);
+ return ERR_ADDRESS_INVALID;
}
scoped_ptr<TCPSocketLibevent> tcp_socket(new TCPSocketLibevent(
net_log_.net_log(), net_log_.source()));
- int adopt_result = tcp_socket->Adopt(new_socket);
+ int adopt_result = tcp_socket->AdoptConnectedSocket(new_socket, ip_end_point);
if (adopt_result != OK) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result);
return adopt_result;
@@ -229,7 +596,183 @@ int TCPSocketLibevent::AcceptInternal(scoped_ptr<TCPSocketLibevent>* socket,
return OK;
}
-void TCPSocketLibevent::OnFileCanReadWithoutBlocking(int fd) {
+int TCPSocketLibevent::DoConnect() {
+ DCHECK_EQ(0, connect_os_error_);
+
+ net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+ CreateNetLogIPEndPointCallback(peer_address_.get()));
+
+ // Connect the socket.
+ if (!use_tcp_fastopen_) {
+ SockaddrStorage storage;
+ if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len))
+ return ERR_INVALID_ARGUMENT;
+
+ if (!HANDLE_EINTR(connect(socket_, storage.addr, storage.addr_len))) {
+ // Connected without waiting!
+ return OK;
+ }
+ } else {
+ // With TCP FastOpen, we pretend that the socket is connected.
+ DCHECK(!tcp_fastopen_connected_);
+ return OK;
+ }
+
+ // Check if the connect() failed synchronously.
+ connect_os_error_ = errno;
+ if (connect_os_error_ != EINPROGRESS)
+ return MapConnectError(connect_os_error_);
+
+ // Otherwise the connect() is going to complete asynchronously, so watch
+ // for its completion.
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, base::MessageLoopForIO::WATCH_WRITE,
+ &write_socket_watcher_, &write_watcher_)) {
+ connect_os_error_ = errno;
+ DVLOG(1) << "WatchFileDescriptor failed: " << connect_os_error_;
+ return MapSystemError(connect_os_error_);
+ }
+
+ return ERR_IO_PENDING;
+}
+
+void TCPSocketLibevent::DoConnectComplete(int result) {
+ // Log the end of this attempt (and any OS error it threw).
+ int os_error = connect_os_error_;
+ connect_os_error_ = 0;
+ if (result != OK) {
+ net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+ NetLog::IntegerCallback("os_error", os_error));
+ } else {
+ net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT);
+ }
+
+ if (!logging_multiple_connect_attempts_)
+ LogConnectEnd(result);
+}
+
+void TCPSocketLibevent::LogConnectBegin(const AddressList& addresses) {
+ base::StatsCounter connects("tcp.connect");
+ connects.Increment();
+
+ net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
+ addresses.CreateNetLogCallback());
+}
+
+void TCPSocketLibevent::LogConnectEnd(int net_error) {
+ if (net_error == OK)
+ UpdateConnectionTypeHistograms(CONNECTION_ANY);
+
+ if (net_error != OK) {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error);
+ return;
+ }
+
+ SockaddrStorage storage;
+ int rv = getsockname(socket_, storage.addr, &storage.addr_len);
+ if (rv != 0) {
+ PLOG(ERROR) << "getsockname() [rv: " << rv << "] error: ";
+ NOTREACHED();
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv);
+ return;
+ }
+
+ net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT,
+ CreateNetLogSourceAddressCallback(storage.addr,
+ storage.addr_len));
+}
+
+void TCPSocketLibevent::DidCompleteRead() {
+ RecordFastOpenStatus();
+ if (read_callback_.is_null())
+ return;
+
+ int bytes_transferred;
+ bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(),
+ read_buf_len_));
+
+ int result;
+ if (bytes_transferred >= 0) {
+ result = bytes_transferred;
+ base::StatsCounter read_bytes("tcp.read_bytes");
+ read_bytes.Add(bytes_transferred);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, result,
+ read_buf_->data());
+ } else {
+ result = MapSystemError(errno);
+ if (result != ERR_IO_PENDING) {
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
+ CreateNetLogSocketErrorCallback(result, errno));
+ }
+ }
+
+ if (result != ERR_IO_PENDING) {
+ read_buf_ = NULL;
+ read_buf_len_ = 0;
+ bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ base::ResetAndReturn(&read_callback_).Run(result);
+ }
+}
+
+void TCPSocketLibevent::DidCompleteWrite() {
+ if (write_callback_.is_null())
+ return;
+
+ int bytes_transferred;
+ bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(),
+ write_buf_len_));
+
+ int result;
+ if (bytes_transferred >= 0) {
+ result = bytes_transferred;
+ base::StatsCounter write_bytes("tcp.write_bytes");
+ write_bytes.Add(bytes_transferred);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, result,
+ write_buf_->data());
+ } else {
+ result = MapSystemError(errno);
+ if (result != ERR_IO_PENDING) {
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
+ CreateNetLogSocketErrorCallback(result, errno));
+ }
+ }
+
+ if (result != ERR_IO_PENDING) {
+ write_buf_ = NULL;
+ write_buf_len_ = 0;
+ write_socket_watcher_.StopWatchingFileDescriptor();
+ base::ResetAndReturn(&write_callback_).Run(result);
+ }
+}
+
+void TCPSocketLibevent::DidCompleteConnect() {
+ DCHECK(waiting_connect_);
+
+ // Get the error that connect() completed with.
+ int os_error = 0;
+ socklen_t len = sizeof(os_error);
+ if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0)
+ os_error = errno;
+
+ int result = MapConnectError(os_error);
+ connect_os_error_ = os_error;
+ if (result != ERR_IO_PENDING) {
+ DoConnectComplete(result);
+ waiting_connect_ = false;
+ write_socket_watcher_.StopWatchingFileDescriptor();
+ base::ResetAndReturn(&write_callback_).Run(result);
+ }
+}
+
+void TCPSocketLibevent::DidCompleteConnectOrWrite() {
+ if (waiting_connect_)
+ DidCompleteConnect();
+ else
+ DidCompleteWrite();
+}
+
+void TCPSocketLibevent::DidCompleteAccept() {
DCHECK(CalledOnValidThread());
int result = AcceptInternal(accept_socket_, accept_address_);
@@ -244,8 +787,85 @@ void TCPSocketLibevent::OnFileCanReadWithoutBlocking(int fd) {
}
}
-void TCPSocketLibevent::OnFileCanWriteWithoutBlocking(int fd) {
- NOTREACHED();
+int TCPSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) {
+ int nwrite;
+ if (use_tcp_fastopen_ && !tcp_fastopen_connected_) {
+ SockaddrStorage storage;
+ if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int flags = 0x20000000; // Magic flag to enable TCP_FASTOPEN.
+#if defined(OS_LINUX)
+ // sendto() will fail with EPIPE when the system doesn't support TCP Fast
+ // Open. Theoretically that shouldn't happen since the caller should check
+ // for system support on startup, but users may dynamically disable TCP Fast
+ // Open via sysctl.
+ flags |= MSG_NOSIGNAL;
+#endif // defined(OS_LINUX)
+ nwrite = HANDLE_EINTR(sendto(socket_,
+ buf->data(),
+ buf_len,
+ flags,
+ storage.addr,
+ storage.addr_len));
+ tcp_fastopen_connected_ = true;
+
+ if (nwrite < 0) {
+ DCHECK_NE(EPIPE, errno);
+
+ // If errno == EINPROGRESS, that means the kernel didn't have a cookie
+ // and would block. The kernel is internally doing a connect() though.
+ // Remap EINPROGRESS to EAGAIN so we treat this the same as our other
+ // asynchronous cases. Note that the user buffer has not been copied to
+ // kernel space.
+ if (errno == EINPROGRESS) {
+ errno = EAGAIN;
+ fast_open_status_ = FAST_OPEN_SLOW_CONNECT_RETURN;
+ } else {
+ fast_open_status_ = FAST_OPEN_ERROR;
+ }
+ } else {
+ fast_open_status_ = FAST_OPEN_FAST_CONNECT_RETURN;
+ }
+ } else {
+ nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
+ }
+ return nwrite;
+}
+
+void TCPSocketLibevent::RecordFastOpenStatus() {
+ if (use_tcp_fastopen_ &&
+ (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN ||
+ fast_open_status_ == FAST_OPEN_SLOW_CONNECT_RETURN)) {
+ DCHECK_NE(FAST_OPEN_STATUS_UNKNOWN, fast_open_status_);
+ bool getsockopt_success(false);
+ bool server_acked_data(false);
+#if defined(TCP_INFO)
+ // Probe to see the if the socket used TCP Fast Open.
+ tcp_info info;
+ socklen_t info_len = sizeof(tcp_info);
+ getsockopt_success =
+ getsockopt(socket_, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0 &&
+ info_len == sizeof(tcp_info);
+ server_acked_data = getsockopt_success &&
+ (info.tcpi_options & TCPI_OPT_SYN_DATA);
+#endif
+ if (getsockopt_success) {
+ if (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN) {
+ fast_open_status_ = (server_acked_data ? FAST_OPEN_SYN_DATA_ACK :
+ FAST_OPEN_SYN_DATA_NACK);
+ } else {
+ fast_open_status_ = (server_acked_data ? FAST_OPEN_NO_SYN_DATA_ACK :
+ FAST_OPEN_NO_SYN_DATA_NACK);
+ }
+ } else {
+ fast_open_status_ = (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN ?
+ FAST_OPEN_SYN_DATA_FAILED :
+ FAST_OPEN_NO_SYN_DATA_FAILED);
+ }
+ }
}
} // namespace net
diff --git a/net/socket/tcp_socket_libevent.h b/net/socket/tcp_socket_libevent.h
index 91a3738f9c..a50caf0ad5 100644
--- a/net/socket/tcp_socket_libevent.h
+++ b/net/socket/tcp_socket_libevent.h
@@ -6,7 +6,9 @@
#define NET_SOCKET_TCP_SOCKET_LIBEVENT_H_
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/non_thread_safe.h"
@@ -14,54 +16,215 @@
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
+#include "net/socket/socket_descriptor.h"
namespace net {
+class AddressList;
+class IOBuffer;
class IPEndPoint;
-// TODO(yzshen): This class is incomplete. TCP client operations (Connect/Read/
-// Write/etc.) will be added. And TCPClientSocket will be changed to be a
-// wrapper around TCPSocket.
-class NET_EXPORT TCPSocketLibevent : public base::NonThreadSafe,
- public base::MessageLoopForIO::Watcher {
+class NET_EXPORT TCPSocketLibevent : public base::NonThreadSafe {
public:
TCPSocketLibevent(NetLog* net_log, const NetLog::Source& source);
virtual ~TCPSocketLibevent();
- int Create(AddressFamily family);
+ int Open(AddressFamily family);
// Takes ownership of |socket|.
- int Adopt(int socket);
- // Returns a socket file descriptor. The ownership is transferred to the
- // caller.
- int Release();
+ int AdoptConnectedSocket(int socket, const IPEndPoint& peer_address);
+
int Bind(const IPEndPoint& address);
- int GetLocalAddress(IPEndPoint* address) const;
+
int Listen(int backlog);
int Accept(scoped_ptr<TCPSocketLibevent>* socket,
IPEndPoint* address,
const CompletionCallback& callback);
+
+ int Connect(const IPEndPoint& address, const CompletionCallback& callback);
+ bool IsConnected() const;
+ bool IsConnectedAndIdle() const;
+
+ // Multiple outstanding requests are not supported.
+ // Full duplex mode (reading and writing at the same time) is supported.
+ int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
+ int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
+
+ int GetLocalAddress(IPEndPoint* address) const;
+ int GetPeerAddress(IPEndPoint* address) const;
+
+ // Sets various socket options.
+ // The commonly used options for server listening sockets:
+ // - SetAddressReuse(true).
int SetDefaultOptionsForServer();
+ // The commonly used options for client sockets and accepted sockets:
+ // - SetNoDelay(true);
+ // - SetKeepAlive(true, 45).
+ void SetDefaultOptionsForClient();
int SetAddressReuse(bool allow);
+ bool SetReceiveBufferSize(int32 size);
+ bool SetSendBufferSize(int32 size);
+ bool SetKeepAlive(bool enable, int delay);
+ bool SetNoDelay(bool no_delay);
+
void Close();
- const BoundNetLog& net_log() const { return net_log_; }
+ bool UsingTCPFastOpen() const;
+ bool IsValid() const { return socket_ != kInvalidSocket; }
+
+ // Marks the start/end of a series of connect attempts for logging purpose.
+ //
+ // TCPClientSocket may attempt to connect to multiple addresses until it
+ // succeeds in establishing a connection. The corresponding log will have
+ // multiple NetLog::TYPE_TCP_CONNECT_ATTEMPT entries nested within a
+ // NetLog::TYPE_TCP_CONNECT. These methods set the start/end of
+ // NetLog::TYPE_TCP_CONNECT.
+ //
+ // TODO(yzshen): Change logging format and let TCPClientSocket log the
+ // start/end of a series of connect attempts itself.
+ void StartLoggingMultipleConnectAttempts(const AddressList& addresses);
+ void EndLoggingMultipleConnectAttempts(int net_error);
- // MessageLoopForIO::Watcher implementation.
- virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
- virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+ const BoundNetLog& net_log() const { return net_log_; }
private:
+ // States that a fast open socket attempt can result in.
+ enum FastOpenStatus {
+ FAST_OPEN_STATUS_UNKNOWN,
+
+ // The initial fast open connect attempted returned synchronously,
+ // indicating that we had and sent a cookie along with the initial data.
+ FAST_OPEN_FAST_CONNECT_RETURN,
+
+ // The initial fast open connect attempted returned asynchronously,
+ // indicating that we did not have a cookie for the server.
+ FAST_OPEN_SLOW_CONNECT_RETURN,
+
+ // Some other error occurred on connection, so we couldn't tell if
+ // fast open would have worked.
+ FAST_OPEN_ERROR,
+
+ // An attempt to do a fast open succeeded immediately
+ // (FAST_OPEN_FAST_CONNECT_RETURN) and we later confirmed that the server
+ // had acked the data we sent.
+ FAST_OPEN_SYN_DATA_ACK,
+
+ // An attempt to do a fast open succeeded immediately
+ // (FAST_OPEN_FAST_CONNECT_RETURN) and we later confirmed that the server
+ // had nacked the data we sent.
+ FAST_OPEN_SYN_DATA_NACK,
+
+ // An attempt to do a fast open succeeded immediately
+ // (FAST_OPEN_FAST_CONNECT_RETURN) and our probe to determine if the
+ // socket was using fast open failed.
+ FAST_OPEN_SYN_DATA_FAILED,
+
+ // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
+ // and we later confirmed that the server had acked initial data. This
+ // should never happen (we didn't send data, so it shouldn't have
+ // been acked).
+ FAST_OPEN_NO_SYN_DATA_ACK,
+
+ // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
+ // and we later discovered that the server had nacked initial data. This
+ // is the expected case results for FAST_OPEN_SLOW_CONNECT_RETURN.
+ FAST_OPEN_NO_SYN_DATA_NACK,
+
+ // An attempt to do a fast open failed (FAST_OPEN_SLOW_CONNECT_RETURN)
+ // and our later probe for ack/nack state failed.
+ FAST_OPEN_NO_SYN_DATA_FAILED,
+
+ FAST_OPEN_MAX_VALUE
+ };
+
+ // Watcher simply forwards notifications to Closure objects set via the
+ // constructor.
+ class Watcher: public base::MessageLoopForIO::Watcher {
+ public:
+ Watcher(const base::Closure& read_ready_callback,
+ const base::Closure& write_ready_callback);
+ virtual ~Watcher();
+
+ // base::MessageLoopForIO::Watcher methods.
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ base::Closure read_ready_callback_;
+ base::Closure write_ready_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Watcher);
+ };
+
int AcceptInternal(scoped_ptr<TCPSocketLibevent>* socket,
IPEndPoint* address);
+ int DoConnect();
+ void DoConnectComplete(int result);
+
+ void LogConnectBegin(const AddressList& addresses);
+ void LogConnectEnd(int net_error);
+
+ void DidCompleteRead();
+ void DidCompleteWrite();
+ void DidCompleteConnect();
+ void DidCompleteConnectOrWrite();
+ void DidCompleteAccept();
+
+ // Internal function to write to a socket. Returns an OS error.
+ int InternalWrite(IOBuffer* buf, int buf_len);
+
+ // Called when the socket is known to be in a connected state.
+ void RecordFastOpenStatus();
+
int socket_;
base::MessageLoopForIO::FileDescriptorWatcher accept_socket_watcher_;
+ Watcher accept_watcher_;
scoped_ptr<TCPSocketLibevent>* accept_socket_;
IPEndPoint* accept_address_;
CompletionCallback accept_callback_;
+ // The socket's libevent wrappers for reads and writes.
+ base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
+ base::MessageLoopForIO::FileDescriptorWatcher write_socket_watcher_;
+
+ // The corresponding watchers for reads and writes.
+ Watcher read_watcher_;
+ Watcher write_watcher_;
+
+ // The buffer used for reads.
+ scoped_refptr<IOBuffer> read_buf_;
+ int read_buf_len_;
+
+ // The buffer used for writes.
+ scoped_refptr<IOBuffer> write_buf_;
+ int write_buf_len_;
+
+ // External callback; called when read is complete.
+ CompletionCallback read_callback_;
+
+ // External callback; called when write or connect is complete.
+ CompletionCallback write_callback_;
+
+ // Enables experimental TCP FastOpen option.
+ const bool use_tcp_fastopen_;
+
+ // True when TCP FastOpen is in use and we have done the connect.
+ bool tcp_fastopen_connected_;
+
+ FastOpenStatus fast_open_status_;
+
+ // A connect operation is pending. In this case, |write_callback_| needs to be
+ // called when connect is complete.
+ bool waiting_connect_;
+
+ scoped_ptr<IPEndPoint> peer_address_;
+ // The OS error that a connect attempt last completed with.
+ int connect_os_error_;
+
+ bool logging_multiple_connect_attempts_;
+
BoundNetLog net_log_;
DISALLOW_COPY_AND_ASSIGN(TCPSocketLibevent);
diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc
index e20bdd8758..a45fcba016 100644
--- a/net/socket/tcp_socket_unittest.cc
+++ b/net/socket/tcp_socket_unittest.cc
@@ -4,10 +4,15 @@
#include "net/socket/tcp_socket.h"
+#include <string.h>
+
#include <string>
+#include <vector>
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/address_list.h"
+#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
@@ -29,7 +34,7 @@ class TCPSocketTest : public PlatformTest {
IPEndPoint address;
ParseAddress("127.0.0.1", 0, &address);
- ASSERT_EQ(OK, socket_.Create(ADDRESS_FAMILY_IPV4));
+ ASSERT_EQ(OK, socket_.Open(ADDRESS_FAMILY_IPV4));
ASSERT_EQ(OK, socket_.Bind(address));
ASSERT_EQ(OK, socket_.Listen(kListenBacklog));
ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_));
@@ -40,7 +45,7 @@ class TCPSocketTest : public PlatformTest {
IPEndPoint address;
ParseAddress("::1", 0, &address);
- if (socket_.Create(ADDRESS_FAMILY_IPV6) != OK ||
+ if (socket_.Open(ADDRESS_FAMILY_IPV6) != OK ||
socket_.Bind(address) != OK ||
socket_.Listen(kListenBacklog) != OK) {
LOG(ERROR) << "Failed to listen on ::1 - probably because IPv6 is "
@@ -194,5 +199,65 @@ TEST_F(TCPSocketTest, AcceptIPv6) {
EXPECT_EQ(OK, connect_callback.WaitForResult());
}
+TEST_F(TCPSocketTest, ReadWrite) {
+ ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4());
+
+ TestCompletionCallback connect_callback;
+ TCPSocket connecting_socket(NULL, NetLog::Source());
+ int result = connecting_socket.Open(ADDRESS_FAMILY_IPV4);
+ ASSERT_EQ(OK, result);
+ connecting_socket.Connect(local_address_, connect_callback.callback());
+
+ TestCompletionCallback accept_callback;
+ scoped_ptr<TCPSocket> accepted_socket;
+ IPEndPoint accepted_address;
+ result = socket_.Accept(&accepted_socket, &accepted_address,
+ accept_callback.callback());
+ ASSERT_EQ(OK, accept_callback.GetResult(result));
+
+ ASSERT_TRUE(accepted_socket.get());
+
+ // Both sockets should be on the loopback network interface.
+ EXPECT_EQ(accepted_address.address(), local_address_.address());
+
+ EXPECT_EQ(OK, connect_callback.WaitForResult());
+
+ const std::string message("test message");
+ std::vector<char> buffer(message.size());
+
+ size_t bytes_written = 0;
+ while (bytes_written < message.size()) {
+ scoped_refptr<IOBufferWithSize> write_buffer(
+ new IOBufferWithSize(message.size() - bytes_written));
+ memmove(write_buffer->data(), message.data() + bytes_written,
+ message.size() - bytes_written);
+
+ TestCompletionCallback write_callback;
+ int write_result = accepted_socket->Write(
+ write_buffer.get(), write_buffer->size(), write_callback.callback());
+ write_result = write_callback.GetResult(write_result);
+ ASSERT_TRUE(write_result >= 0);
+ bytes_written += write_result;
+ ASSERT_TRUE(bytes_written <= message.size());
+ }
+
+ size_t bytes_read = 0;
+ while (bytes_read < message.size()) {
+ scoped_refptr<IOBufferWithSize> read_buffer(
+ new IOBufferWithSize(message.size() - bytes_read));
+ TestCompletionCallback read_callback;
+ int read_result = connecting_socket.Read(
+ read_buffer.get(), read_buffer->size(), read_callback.callback());
+ read_result = read_callback.GetResult(read_result);
+ ASSERT_TRUE(read_result >= 0);
+ ASSERT_TRUE(bytes_read + read_result <= message.size());
+ memmove(&buffer[bytes_read], read_buffer->data(), read_result);
+ bytes_read += read_result;
+ }
+
+ std::string received_message(buffer.begin(), buffer.end());
+ ASSERT_EQ(message, received_message);
+}
+
} // namespace
} // namespace net
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index f69c7609e7..7d76232f96 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -6,10 +6,17 @@
#include <mstcpip.h>
+#include "base/callback_helpers.h"
#include "base/logging.h"
+#include "base/metrics/stats_counters.h"
+#include "base/win/windows_version.h"
+#include "net/base/address_list.h"
+#include "net/base/connection_type_histograms.h"
+#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "net/base/network_change_notifier.h"
#include "net/base/winsock_init.h"
#include "net/base/winsock_util.h"
#include "net/socket/socket_descriptor.h"
@@ -17,12 +24,251 @@
namespace net {
+namespace {
+
+const int kTCPKeepAliveSeconds = 45;
+
+bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) {
+ int rv = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError();
+ return rv == 0;
+}
+
+bool SetSocketSendBufferSize(SOCKET socket, int32 size) {
+ int rv = setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError();
+ return rv == 0;
+}
+
+// Disable Nagle.
+// The Nagle implementation on windows is governed by RFC 896. The idea
+// behind Nagle is to reduce small packets on the network. When Nagle is
+// enabled, if a partial packet has been sent, the TCP stack will disallow
+// further *partial* packets until an ACK has been received from the other
+// side. Good applications should always strive to send as much data as
+// possible and avoid partial-packet sends. However, in most real world
+// applications, there are edge cases where this does not happen, and two
+// partial packets may be sent back to back. For a browser, it is NEVER
+// a benefit to delay for an RTT before the second packet is sent.
+//
+// As a practical example in Chromium today, consider the case of a small
+// POST. I have verified this:
+// Client writes 649 bytes of header (partial packet #1)
+// Client writes 50 bytes of POST data (partial packet #2)
+// In the above example, with Nagle, a RTT delay is inserted between these
+// two sends due to nagle. RTTs can easily be 100ms or more. The best
+// fix is to make sure that for POSTing data, we write as much data as
+// possible and minimize partial packets. We will fix that. But disabling
+// Nagle also ensure we don't run into this delay in other edge cases.
+// See also:
+// http://technet.microsoft.com/en-us/library/bb726981.aspx
+bool DisableNagle(SOCKET socket, bool disable) {
+ BOOL val = disable ? TRUE : FALSE;
+ int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<const char*>(&val),
+ sizeof(val));
+ DCHECK(!rv) << "Could not disable nagle";
+ return rv == 0;
+}
+
+// Enable TCP Keep-Alive to prevent NAT routers from timing out TCP
+// connections. See http://crbug.com/27400 for details.
+bool SetTCPKeepAlive(SOCKET socket, BOOL enable, int delay_secs) {
+ int delay = delay_secs * 1000;
+ struct tcp_keepalive keepalive_vals = {
+ enable ? 1 : 0, // TCP keep-alive on.
+ delay, // Delay seconds before sending first TCP keep-alive packet.
+ delay, // Delay seconds between sending TCP keep-alive packets.
+ };
+ DWORD bytes_returned = 0xABAB;
+ int rv = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &keepalive_vals,
+ sizeof(keepalive_vals), NULL, 0,
+ &bytes_returned, NULL, NULL);
+ DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket
+ << " [error: " << WSAGetLastError() << "].";
+
+ // Disregard any failure in disabling nagle or enabling TCP Keep-Alive.
+ return rv == 0;
+}
+
+int MapConnectError(int os_error) {
+ switch (os_error) {
+ // connect fails with WSAEACCES when Windows Firewall blocks the
+ // connection.
+ case WSAEACCES:
+ return ERR_NETWORK_ACCESS_DENIED;
+ case WSAETIMEDOUT:
+ return ERR_CONNECTION_TIMED_OUT;
+ default: {
+ int net_error = MapSystemError(os_error);
+ if (net_error == ERR_FAILED)
+ return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
+
+ // Give a more specific error when the user is offline.
+ if (net_error == ERR_ADDRESS_UNREACHABLE &&
+ NetworkChangeNotifier::IsOffline()) {
+ return ERR_INTERNET_DISCONNECTED;
+ }
+
+ return net_error;
+ }
+ }
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+// This class encapsulates all the state that has to be preserved as long as
+// there is a network IO operation in progress. If the owner TCPSocketWin is
+// destroyed while an operation is in progress, the Core is detached and it
+// lives until the operation completes and the OS doesn't reference any resource
+// declared on this class anymore.
+class TCPSocketWin::Core : public base::RefCounted<Core> {
+ public:
+ explicit Core(TCPSocketWin* socket);
+
+ // Start watching for the end of a read or write operation.
+ void WatchForRead();
+ void WatchForWrite();
+
+ // The TCPSocketWin is going away.
+ void Detach() { socket_ = NULL; }
+
+ // The separate OVERLAPPED variables for asynchronous operation.
+ // |read_overlapped_| is used for both Connect() and Read().
+ // |write_overlapped_| is only used for Write();
+ OVERLAPPED read_overlapped_;
+ OVERLAPPED write_overlapped_;
+
+ // The buffers used in Read() and Write().
+ scoped_refptr<IOBuffer> read_iobuffer_;
+ scoped_refptr<IOBuffer> write_iobuffer_;
+ int read_buffer_length_;
+ int write_buffer_length_;
+
+ bool non_blocking_reads_initialized_;
+
+ private:
+ friend class base::RefCounted<Core>;
+
+ class ReadDelegate : public base::win::ObjectWatcher::Delegate {
+ public:
+ explicit ReadDelegate(Core* core) : core_(core) {}
+ virtual ~ReadDelegate() {}
+
+ // base::ObjectWatcher::Delegate methods:
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ Core* const core_;
+ };
+
+ class WriteDelegate : public base::win::ObjectWatcher::Delegate {
+ public:
+ explicit WriteDelegate(Core* core) : core_(core) {}
+ virtual ~WriteDelegate() {}
+
+ // base::ObjectWatcher::Delegate methods:
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ Core* const core_;
+ };
+
+ ~Core();
+
+ // The socket that created this object.
+ TCPSocketWin* socket_;
+
+ // |reader_| handles the signals from |read_watcher_|.
+ ReadDelegate reader_;
+ // |writer_| handles the signals from |write_watcher_|.
+ WriteDelegate writer_;
+
+ // |read_watcher_| watches for events from Connect() and Read().
+ base::win::ObjectWatcher read_watcher_;
+ // |write_watcher_| watches for events from Write();
+ base::win::ObjectWatcher write_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+TCPSocketWin::Core::Core(TCPSocketWin* socket)
+ : read_buffer_length_(0),
+ write_buffer_length_(0),
+ non_blocking_reads_initialized_(false),
+ socket_(socket),
+ reader_(this),
+ writer_(this) {
+ memset(&read_overlapped_, 0, sizeof(read_overlapped_));
+ memset(&write_overlapped_, 0, sizeof(write_overlapped_));
+
+ read_overlapped_.hEvent = WSACreateEvent();
+ write_overlapped_.hEvent = WSACreateEvent();
+}
+
+TCPSocketWin::Core::~Core() {
+ // Make sure the message loop is not watching this object anymore.
+ read_watcher_.StopWatching();
+ write_watcher_.StopWatching();
+
+ WSACloseEvent(read_overlapped_.hEvent);
+ memset(&read_overlapped_, 0xaf, sizeof(read_overlapped_));
+ WSACloseEvent(write_overlapped_.hEvent);
+ memset(&write_overlapped_, 0xaf, sizeof(write_overlapped_));
+}
+
+void TCPSocketWin::Core::WatchForRead() {
+ // We grab an extra reference because there is an IO operation in progress.
+ // Balanced in ReadDelegate::OnObjectSignaled().
+ AddRef();
+ read_watcher_.StartWatching(read_overlapped_.hEvent, &reader_);
+}
+
+void TCPSocketWin::Core::WatchForWrite() {
+ // We grab an extra reference because there is an IO operation in progress.
+ // Balanced in WriteDelegate::OnObjectSignaled().
+ AddRef();
+ write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_);
+}
+
+void TCPSocketWin::Core::ReadDelegate::OnObjectSignaled(HANDLE object) {
+ DCHECK_EQ(object, core_->read_overlapped_.hEvent);
+ if (core_->socket_) {
+ if (core_->socket_->waiting_connect_)
+ core_->socket_->DidCompleteConnect();
+ else
+ core_->socket_->DidSignalRead();
+ }
+
+ core_->Release();
+}
+
+void TCPSocketWin::Core::WriteDelegate::OnObjectSignaled(
+ HANDLE object) {
+ DCHECK_EQ(object, core_->write_overlapped_.hEvent);
+ if (core_->socket_)
+ core_->socket_->DidCompleteWrite();
+
+ core_->Release();
+}
+
+//-----------------------------------------------------------------------------
+
TCPSocketWin::TCPSocketWin(net::NetLog* net_log,
const net::NetLog::Source& source)
: socket_(INVALID_SOCKET),
- socket_event_(WSA_INVALID_EVENT),
+ accept_event_(WSA_INVALID_EVENT),
accept_socket_(NULL),
accept_address_(NULL),
+ waiting_connect_(false),
+ waiting_read_(false),
+ waiting_write_(false),
+ connect_os_error_(0),
+ logging_multiple_connect_attempts_(false),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) {
net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
source.ToEventParametersCallback());
@@ -34,7 +280,7 @@ TCPSocketWin::~TCPSocketWin() {
net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
}
-int TCPSocketWin::Create(AddressFamily family) {
+int TCPSocketWin::Open(AddressFamily family) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(socket_, INVALID_SOCKET);
@@ -54,9 +300,11 @@ int TCPSocketWin::Create(AddressFamily family) {
return OK;
}
-int TCPSocketWin::Adopt(SOCKET socket) {
+int TCPSocketWin::AdoptConnectedSocket(SOCKET socket,
+ const IPEndPoint& peer_address) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(socket_, INVALID_SOCKET);
+ DCHECK(!core_);
socket_ = socket;
@@ -66,17 +314,10 @@ int TCPSocketWin::Adopt(SOCKET socket) {
return result;
}
- return OK;
-}
-
-SOCKET TCPSocketWin::Release() {
- DCHECK(CalledOnValidThread());
- DCHECK_EQ(socket_event_, WSA_INVALID_EVENT);
- DCHECK(accept_callback_.is_null());
+ core_ = new Core(this);
+ peer_address_.reset(new IPEndPoint(peer_address));
- SOCKET result = socket_;
- socket_ = INVALID_SOCKET;
- return result;
+ return OK;
}
int TCPSocketWin::Bind(const IPEndPoint& address) {
@@ -96,36 +337,22 @@ int TCPSocketWin::Bind(const IPEndPoint& address) {
return OK;
}
-int TCPSocketWin::GetLocalAddress(IPEndPoint* address) const {
- DCHECK(CalledOnValidThread());
- DCHECK(address);
-
- SockaddrStorage storage;
- if (getsockname(socket_, storage.addr, &storage.addr_len))
- return MapSystemError(WSAGetLastError());
- if (!address->FromSockAddr(storage.addr, storage.addr_len))
- return ERR_FAILED;
-
- return OK;
-}
-
int TCPSocketWin::Listen(int backlog) {
DCHECK(CalledOnValidThread());
DCHECK_GT(backlog, 0);
DCHECK_NE(socket_, INVALID_SOCKET);
- DCHECK_EQ(socket_event_, WSA_INVALID_EVENT);
+ DCHECK_EQ(accept_event_, WSA_INVALID_EVENT);
- socket_event_ = WSACreateEvent();
- if (socket_event_ == WSA_INVALID_EVENT) {
+ accept_event_ = WSACreateEvent();
+ if (accept_event_ == WSA_INVALID_EVENT) {
PLOG(ERROR) << "WSACreateEvent()";
- return ERR_FAILED;
+ return MapSystemError(WSAGetLastError());
}
int result = listen(socket_, backlog);
if (result < 0) {
PLOG(ERROR) << "listen() returned an error";
- result = MapSystemError(WSAGetLastError());
- return result;
+ return MapSystemError(WSAGetLastError());
}
return OK;
@@ -146,8 +373,8 @@ int TCPSocketWin::Accept(scoped_ptr<TCPSocketWin>* socket,
if (result == ERR_IO_PENDING) {
// Start watching.
- WSAEventSelect(socket_, socket_event_, FD_ACCEPT);
- accept_watcher_.StartWatching(socket_event_, this);
+ WSAEventSelect(socket_, accept_event_, FD_ACCEPT);
+ accept_watcher_.StartWatching(accept_event_, this);
accept_socket_ = socket;
accept_address_ = address;
@@ -157,10 +384,195 @@ int TCPSocketWin::Accept(scoped_ptr<TCPSocketWin>* socket,
return result;
}
+int TCPSocketWin::Connect(const IPEndPoint& address,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(socket_, INVALID_SOCKET);
+ DCHECK(!waiting_connect_);
+
+ // |peer_address_| and |core_| will be non-NULL if Connect() has been called.
+ // Unless Close() is called to reset the internal state, a second call to
+ // Connect() is not allowed.
+ // Please note that we enforce this even if the previous Connect() has
+ // completed and failed. Although it is allowed to connect the same |socket_|
+ // again after a connection attempt failed on Windows, it results in
+ // unspecified behavior according to POSIX. Therefore, we make it behave in
+ // the same way as TCPSocketLibevent.
+ DCHECK(!peer_address_ && !core_);
+
+ if (!logging_multiple_connect_attempts_)
+ LogConnectBegin(AddressList(address));
+
+ peer_address_.reset(new IPEndPoint(address));
+
+ int rv = DoConnect();
+ if (rv == ERR_IO_PENDING) {
+ // Synchronous operation not supported.
+ DCHECK(!callback.is_null());
+ read_callback_ = callback;
+ waiting_connect_ = true;
+ } else {
+ DoConnectComplete(rv);
+ }
+
+ return rv;
+}
+
+bool TCPSocketWin::IsConnected() const {
+ DCHECK(CalledOnValidThread());
+
+ if (socket_ == INVALID_SOCKET || waiting_connect_)
+ return false;
+
+ if (waiting_read_)
+ return true;
+
+ // Check if connection is alive.
+ char c;
+ int rv = recv(socket_, &c, 1, MSG_PEEK);
+ if (rv == 0)
+ return false;
+ if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+bool TCPSocketWin::IsConnectedAndIdle() const {
+ DCHECK(CalledOnValidThread());
+
+ if (socket_ == INVALID_SOCKET || waiting_connect_)
+ return false;
+
+ if (waiting_read_)
+ return true;
+
+ // Check if connection is alive and we haven't received any data
+ // unexpectedly.
+ char c;
+ int rv = recv(socket_, &c, 1, MSG_PEEK);
+ if (rv >= 0)
+ return false;
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+int TCPSocketWin::Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(socket_, INVALID_SOCKET);
+ DCHECK(!waiting_read_);
+ DCHECK(read_callback_.is_null());
+ DCHECK(!core_->read_iobuffer_);
+
+ return DoRead(buf, buf_len, callback);
+}
+
+int TCPSocketWin::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_NE(socket_, INVALID_SOCKET);
+ DCHECK(!waiting_write_);
+ DCHECK(write_callback_.is_null());
+ DCHECK_GT(buf_len, 0);
+ DCHECK(!core_->write_iobuffer_);
+
+ base::StatsCounter writes("tcp.writes");
+ writes.Increment();
+
+ WSABUF write_buffer;
+ write_buffer.len = buf_len;
+ write_buffer.buf = buf->data();
+
+ // TODO(wtc): Remove the assertion after enough testing.
+ AssertEventNotSignaled(core_->write_overlapped_.hEvent);
+ DWORD num;
+ int rv = WSASend(socket_, &write_buffer, 1, &num, 0,
+ &core_->write_overlapped_, NULL);
+ if (rv == 0) {
+ if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) {
+ rv = static_cast<int>(num);
+ if (rv > buf_len || rv < 0) {
+ // It seems that some winsock interceptors report that more was written
+ // than was available. Treat this as an error. http://crbug.com/27870
+ LOG(ERROR) << "Detected broken LSP: Asked to write " << buf_len
+ << " bytes, but " << rv << " bytes reported.";
+ return ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
+ }
+ base::StatsCounter write_bytes("tcp.write_bytes");
+ write_bytes.Add(rv);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv,
+ buf->data());
+ return rv;
+ }
+ } else {
+ int os_error = WSAGetLastError();
+ if (os_error != WSA_IO_PENDING) {
+ int net_error = MapSystemError(os_error);
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
+ CreateNetLogSocketErrorCallback(net_error, os_error));
+ return net_error;
+ }
+ }
+ waiting_write_ = true;
+ write_callback_ = callback;
+ core_->write_iobuffer_ = buf;
+ core_->write_buffer_length_ = buf_len;
+ core_->WatchForWrite();
+ return ERR_IO_PENDING;
+}
+
+int TCPSocketWin::GetLocalAddress(IPEndPoint* address) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(address);
+
+ SockaddrStorage storage;
+ if (getsockname(socket_, storage.addr, &storage.addr_len))
+ return MapSystemError(WSAGetLastError());
+ if (!address->FromSockAddr(storage.addr, storage.addr_len))
+ return ERR_ADDRESS_INVALID;
+
+ return OK;
+}
+
+int TCPSocketWin::GetPeerAddress(IPEndPoint* address) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(address);
+ if (!IsConnected())
+ return ERR_SOCKET_NOT_CONNECTED;
+ *address = *peer_address_;
+ return OK;
+}
+
int TCPSocketWin::SetDefaultOptionsForServer() {
return SetExclusiveAddrUse();
}
+void TCPSocketWin::SetDefaultOptionsForClient() {
+ // Increase the socket buffer sizes from the default sizes for WinXP. In
+ // performance testing, there is substantial benefit by increasing from 8KB
+ // to 64KB.
+ // See also:
+ // http://support.microsoft.com/kb/823764/EN-US
+ // On Vista, if we manually set these sizes, Vista turns off its receive
+ // window auto-tuning feature.
+ // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx
+ // Since Vista's auto-tune is better than any static value we can could set,
+ // only change these on pre-vista machines.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ const int32 kSocketBufferSize = 64 * 1024;
+ SetSocketReceiveBufferSize(socket_, kSocketBufferSize);
+ SetSocketSendBufferSize(socket_, kSocketBufferSize);
+ }
+
+ DisableNagle(socket_, true);
+ SetTCPKeepAlive(socket_, true, kTCPKeepAliveSeconds);
+}
+
int TCPSocketWin::SetExclusiveAddrUse() {
// On Windows, a bound end point can be hijacked by another process by
// setting SO_REUSEADDR. Therefore a Windows-only option SO_EXCLUSIVEADDRUSE
@@ -187,16 +599,99 @@ int TCPSocketWin::SetExclusiveAddrUse() {
return OK;
}
+bool TCPSocketWin::SetReceiveBufferSize(int32 size) {
+ DCHECK(CalledOnValidThread());
+ return SetSocketReceiveBufferSize(socket_, size);
+}
+
+bool TCPSocketWin::SetSendBufferSize(int32 size) {
+ DCHECK(CalledOnValidThread());
+ return SetSocketSendBufferSize(socket_, size);
+}
+
+bool TCPSocketWin::SetKeepAlive(bool enable, int delay) {
+ return SetTCPKeepAlive(socket_, enable, delay);
+}
+
+bool TCPSocketWin::SetNoDelay(bool no_delay) {
+ return DisableNagle(socket_, no_delay);
+}
+
void TCPSocketWin::Close() {
+ DCHECK(CalledOnValidThread());
+
if (socket_ != INVALID_SOCKET) {
+ // Note: don't use CancelIo to cancel pending IO because it doesn't work
+ // when there is a Winsock layered service provider.
+
+ // In most socket implementations, closing a socket results in a graceful
+ // connection shutdown, but in Winsock we have to call shutdown explicitly.
+ // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
+ // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
+ shutdown(socket_, SD_SEND);
+
+ // This cancels any pending IO.
if (closesocket(socket_) < 0)
PLOG(ERROR) << "closesocket";
socket_ = INVALID_SOCKET;
}
- if (socket_event_) {
- WSACloseEvent(socket_event_);
- socket_event_ = WSA_INVALID_EVENT;
+ if (accept_event_) {
+ WSACloseEvent(accept_event_);
+ accept_event_ = WSA_INVALID_EVENT;
+ }
+
+ if (!accept_callback_.is_null()) {
+ accept_watcher_.StopWatching();
+ accept_socket_ = NULL;
+ accept_address_ = NULL;
+ accept_callback_.Reset();
+ }
+
+ if (core_) {
+ if (waiting_connect_) {
+ // We closed the socket, so this notification will never come.
+ // From MSDN' WSAEventSelect documentation:
+ // "Closing a socket with closesocket also cancels the association and
+ // selection of network events specified in WSAEventSelect for the
+ // socket".
+ core_->Release();
+ }
+ core_->Detach();
+ core_ = NULL;
+ }
+
+ waiting_connect_ = false;
+ waiting_read_ = false;
+ waiting_write_ = false;
+
+ read_callback_.Reset();
+ write_callback_.Reset();
+ peer_address_.reset();
+ connect_os_error_ = 0;
+}
+
+bool TCPSocketWin::UsingTCPFastOpen() const {
+ // Not supported on windows.
+ return false;
+}
+
+void TCPSocketWin::StartLoggingMultipleConnectAttempts(
+ const AddressList& addresses) {
+ if (!logging_multiple_connect_attempts_) {
+ logging_multiple_connect_attempts_ = true;
+ LogConnectBegin(addresses);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void TCPSocketWin::EndLoggingMultipleConnectAttempts(int net_error) {
+ if (logging_multiple_connect_attempts_) {
+ LogConnectEnd(net_error);
+ logging_multiple_connect_attempts_ = false;
+ } else {
+ NOTREACHED();
}
}
@@ -221,7 +716,7 @@ int TCPSocketWin::AcceptInternal(scoped_ptr<TCPSocketWin>* socket,
}
scoped_ptr<TCPSocketWin> tcp_socket(new TCPSocketWin(
net_log_.net_log(), net_log_.source()));
- int adopt_result = tcp_socket->Adopt(new_socket);
+ int adopt_result = tcp_socket->AdoptConnectedSocket(new_socket, ip_end_point);
if (adopt_result != OK) {
net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result);
return adopt_result;
@@ -235,7 +730,7 @@ int TCPSocketWin::AcceptInternal(scoped_ptr<TCPSocketWin>* socket,
void TCPSocketWin::OnObjectSignaled(HANDLE object) {
WSANETWORKEVENTS ev;
- if (WSAEnumNetworkEvents(socket_, socket_event_, &ev) == SOCKET_ERROR) {
+ if (WSAEnumNetworkEvents(socket_, accept_event_, &ev) == SOCKET_ERROR) {
PLOG(ERROR) << "WSAEnumNetworkEvents()";
return;
}
@@ -245,11 +740,253 @@ void TCPSocketWin::OnObjectSignaled(HANDLE object) {
if (result != ERR_IO_PENDING) {
accept_socket_ = NULL;
accept_address_ = NULL;
- CompletionCallback callback = accept_callback_;
- accept_callback_.Reset();
- callback.Run(result);
+ base::ResetAndReturn(&accept_callback_).Run(result);
+ }
+ }
+}
+
+int TCPSocketWin::DoConnect() {
+ DCHECK_EQ(connect_os_error_, 0);
+ DCHECK(!core_);
+
+ net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+ CreateNetLogIPEndPointCallback(peer_address_.get()));
+
+ core_ = new Core(this);
+ // WSAEventSelect sets the socket to non-blocking mode as a side effect.
+ // Our connect() and recv() calls require that the socket be non-blocking.
+ WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT);
+
+ SockaddrStorage storage;
+ if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len))
+ return ERR_INVALID_ARGUMENT;
+ if (!connect(socket_, storage.addr, storage.addr_len)) {
+ // Connected without waiting!
+ //
+ // The MSDN page for connect says:
+ // With a nonblocking socket, the connection attempt cannot be completed
+ // immediately. In this case, connect will return SOCKET_ERROR, and
+ // WSAGetLastError will return WSAEWOULDBLOCK.
+ // which implies that for a nonblocking socket, connect never returns 0.
+ // It's not documented whether the event object will be signaled or not
+ // if connect does return 0. So the code below is essentially dead code
+ // and we don't know if it's correct.
+ NOTREACHED();
+
+ if (ResetEventIfSignaled(core_->read_overlapped_.hEvent))
+ return OK;
+ } else {
+ int os_error = WSAGetLastError();
+ if (os_error != WSAEWOULDBLOCK) {
+ LOG(ERROR) << "connect failed: " << os_error;
+ connect_os_error_ = os_error;
+ int rv = MapConnectError(os_error);
+ CHECK_NE(ERR_IO_PENDING, rv);
+ return rv;
}
}
+
+ core_->WatchForRead();
+ return ERR_IO_PENDING;
+}
+
+void TCPSocketWin::DoConnectComplete(int result) {
+ // Log the end of this attempt (and any OS error it threw).
+ int os_error = connect_os_error_;
+ connect_os_error_ = 0;
+ if (result != OK) {
+ net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+ NetLog::IntegerCallback("os_error", os_error));
+ } else {
+ net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT);
+ }
+
+ if (!logging_multiple_connect_attempts_)
+ LogConnectEnd(result);
+}
+
+void TCPSocketWin::LogConnectBegin(const AddressList& addresses) {
+ base::StatsCounter connects("tcp.connect");
+ connects.Increment();
+
+ net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
+ addresses.CreateNetLogCallback());
+}
+
+void TCPSocketWin::LogConnectEnd(int net_error) {
+ if (net_error == OK)
+ UpdateConnectionTypeHistograms(CONNECTION_ANY);
+
+ if (net_error != OK) {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error);
+ return;
+ }
+
+ struct sockaddr_storage source_address;
+ socklen_t addrlen = sizeof(source_address);
+ int rv = getsockname(
+ socket_, reinterpret_cast<struct sockaddr*>(&source_address), &addrlen);
+ if (rv != 0) {
+ LOG(ERROR) << "getsockname() [rv: " << rv
+ << "] error: " << WSAGetLastError();
+ NOTREACHED();
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv);
+ return;
+ }
+
+ net_log_.EndEvent(
+ NetLog::TYPE_TCP_CONNECT,
+ CreateNetLogSourceAddressCallback(
+ reinterpret_cast<const struct sockaddr*>(&source_address),
+ sizeof(source_address)));
+}
+
+int TCPSocketWin::DoRead(IOBuffer* buf, int buf_len,
+ const CompletionCallback& callback) {
+ if (!core_->non_blocking_reads_initialized_) {
+ WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
+ FD_READ | FD_CLOSE);
+ core_->non_blocking_reads_initialized_ = true;
+ }
+ int rv = recv(socket_, buf->data(), buf_len, 0);
+ if (rv == SOCKET_ERROR) {
+ int os_error = WSAGetLastError();
+ if (os_error != WSAEWOULDBLOCK) {
+ int net_error = MapSystemError(os_error);
+ net_log_.AddEvent(
+ NetLog::TYPE_SOCKET_READ_ERROR,
+ CreateNetLogSocketErrorCallback(net_error, os_error));
+ return net_error;
+ }
+ } else {
+ base::StatsCounter read_bytes("tcp.read_bytes");
+ if (rv > 0)
+ read_bytes.Add(rv);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
+ buf->data());
+ return rv;
+ }
+
+ waiting_read_ = true;
+ read_callback_ = callback;
+ core_->read_iobuffer_ = buf;
+ core_->read_buffer_length_ = buf_len;
+ core_->WatchForRead();
+ return ERR_IO_PENDING;
+}
+
+void TCPSocketWin::DidCompleteConnect() {
+ DCHECK(waiting_connect_);
+ DCHECK(!read_callback_.is_null());
+ int result;
+
+ WSANETWORKEVENTS events;
+ int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
+ &events);
+ int os_error = 0;
+ if (rv == SOCKET_ERROR) {
+ NOTREACHED();
+ os_error = WSAGetLastError();
+ result = MapSystemError(os_error);
+ } else if (events.lNetworkEvents & FD_CONNECT) {
+ os_error = events.iErrorCode[FD_CONNECT_BIT];
+ result = MapConnectError(os_error);
+ } else {
+ NOTREACHED();
+ result = ERR_UNEXPECTED;
+ }
+
+ connect_os_error_ = os_error;
+ DoConnectComplete(result);
+ waiting_connect_ = false;
+
+ DCHECK_NE(result, ERR_IO_PENDING);
+ base::ResetAndReturn(&read_callback_).Run(result);
+}
+
+void TCPSocketWin::DidCompleteWrite() {
+ DCHECK(waiting_write_);
+ DCHECK(!write_callback_.is_null());
+
+ DWORD num_bytes, flags;
+ BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_,
+ &num_bytes, FALSE, &flags);
+ WSAResetEvent(core_->write_overlapped_.hEvent);
+ waiting_write_ = false;
+ int rv;
+ if (!ok) {
+ int os_error = WSAGetLastError();
+ rv = MapSystemError(os_error);
+ net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR,
+ CreateNetLogSocketErrorCallback(rv, os_error));
+ } else {
+ rv = static_cast<int>(num_bytes);
+ if (rv > core_->write_buffer_length_ || rv < 0) {
+ // It seems that some winsock interceptors report that more was written
+ // than was available. Treat this as an error. http://crbug.com/27870
+ LOG(ERROR) << "Detected broken LSP: Asked to write "
+ << core_->write_buffer_length_ << " bytes, but " << rv
+ << " bytes reported.";
+ rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
+ } else {
+ base::StatsCounter write_bytes("tcp.write_bytes");
+ write_bytes.Add(num_bytes);
+ net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes,
+ core_->write_iobuffer_->data());
+ }
+ }
+
+ core_->write_iobuffer_ = NULL;
+
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ base::ResetAndReturn(&write_callback_).Run(rv);
+}
+
+void TCPSocketWin::DidSignalRead() {
+ DCHECK(waiting_read_);
+ DCHECK(!read_callback_.is_null());
+
+ int os_error = 0;
+ WSANETWORKEVENTS network_events;
+ int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
+ &network_events);
+ if (rv == SOCKET_ERROR) {
+ os_error = WSAGetLastError();
+ rv = MapSystemError(os_error);
+ } else if (network_events.lNetworkEvents) {
+ DCHECK_EQ(network_events.lNetworkEvents & ~(FD_READ | FD_CLOSE), 0);
+ // If network_events.lNetworkEvents is FD_CLOSE and
+ // network_events.iErrorCode[FD_CLOSE_BIT] is 0, it is a graceful
+ // connection closure. It is tempting to directly set rv to 0 in
+ // this case, but the MSDN pages for WSAEventSelect and
+ // WSAAsyncSelect recommend we still call DoRead():
+ // FD_CLOSE should only be posted after all data is read from a
+ // socket, but an application should check for remaining data upon
+ // receipt of FD_CLOSE to avoid any possibility of losing data.
+ //
+ // If network_events.iErrorCode[FD_READ_BIT] or
+ // network_events.iErrorCode[FD_CLOSE_BIT] is nonzero, still call
+ // DoRead() because recv() reports a more accurate error code
+ // (WSAECONNRESET vs. WSAECONNABORTED) when the connection was
+ // reset.
+ rv = DoRead(core_->read_iobuffer_, core_->read_buffer_length_,
+ read_callback_);
+ if (rv == ERR_IO_PENDING)
+ return;
+ } else {
+ // This may happen because Read() may succeed synchronously and
+ // consume all the received data without resetting the event object.
+ core_->WatchForRead();
+ return;
+ }
+
+ waiting_read_ = false;
+ core_->read_iobuffer_ = NULL;
+ core_->read_buffer_length_ = 0;
+
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ base::ResetAndReturn(&read_callback_).Run(rv);
}
} // namespace net
+
diff --git a/net/socket/tcp_socket_win.h b/net/socket/tcp_socket_win.h
index 044e5e0585..df5fbf09ae 100644
--- a/net/socket/tcp_socket_win.h
+++ b/net/socket/tcp_socket_win.h
@@ -9,8 +9,8 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
#include "base/threading/non_thread_safe.h"
#include "base/win/object_watcher.h"
#include "net/base/address_family.h"
@@ -20,50 +20,125 @@
namespace net {
+class AddressList;
+class IOBuffer;
class IPEndPoint;
-// TODO(yzshen): This class is incomplete. TCP client operations (Connect/Read/
-// Write/etc.) will be added. And TCPClientSocket will be changed to be a
-// wrapper around TCPSocket.
class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe),
public base::win::ObjectWatcher::Delegate {
public:
TCPSocketWin(NetLog* net_log, const NetLog::Source& source);
virtual ~TCPSocketWin();
- int Create(AddressFamily family);
+ int Open(AddressFamily family);
// Takes ownership of |socket|.
- int Adopt(SOCKET socket);
- // Returns a socket descriptor. The ownership is transferred to the caller.
- SOCKET Release();
+ int AdoptConnectedSocket(SOCKET socket, const IPEndPoint& peer_address);
+
int Bind(const IPEndPoint& address);
- int GetLocalAddress(IPEndPoint* address) const;
+
int Listen(int backlog);
int Accept(scoped_ptr<TCPSocketWin>* socket,
IPEndPoint* address,
const CompletionCallback& callback);
+
+ int Connect(const IPEndPoint& address, const CompletionCallback& callback);
+ bool IsConnected() const;
+ bool IsConnectedAndIdle() const;
+
+ // Multiple outstanding requests are not supported.
+ // Full duplex mode (reading and writing at the same time) is supported.
+ int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
+ int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
+
+ int GetLocalAddress(IPEndPoint* address) const;
+ int GetPeerAddress(IPEndPoint* address) const;
+
+ // Sets various socket options.
+ // The commonly used options for server listening sockets:
+ // - SetExclusiveAddrUse().
int SetDefaultOptionsForServer();
+ // The commonly used options for client sockets and accepted sockets:
+ // - Increase the socket buffer sizes for WinXP;
+ // - SetNoDelay(true);
+ // - SetKeepAlive(true, 45).
+ void SetDefaultOptionsForClient();
int SetExclusiveAddrUse();
+ bool SetReceiveBufferSize(int32 size);
+ bool SetSendBufferSize(int32 size);
+ bool SetKeepAlive(bool enable, int delay);
+ bool SetNoDelay(bool no_delay);
+
void Close();
+ bool UsingTCPFastOpen() const;
+ bool IsValid() const { return socket_ != INVALID_SOCKET; }
+
+ // Marks the start/end of a series of connect attempts for logging purpose.
+ //
+ // TCPClientSocket may attempt to connect to multiple addresses until it
+ // succeeds in establishing a connection. The corresponding log will have
+ // multiple NetLog::TYPE_TCP_CONNECT_ATTEMPT entries nested within a
+ // NetLog::TYPE_TCP_CONNECT. These methods set the start/end of
+ // NetLog::TYPE_TCP_CONNECT.
+ //
+ // TODO(yzshen): Change logging format and let TCPClientSocket log the
+ // start/end of a series of connect attempts itself.
+ void StartLoggingMultipleConnectAttempts(const AddressList& addresses);
+ void EndLoggingMultipleConnectAttempts(int net_error);
+
const BoundNetLog& net_log() const { return net_log_; }
+ private:
+ class Core;
+
// base::ObjectWatcher::Delegate implementation.
virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
- private:
int AcceptInternal(scoped_ptr<TCPSocketWin>* socket,
IPEndPoint* address);
+ int DoConnect();
+ void DoConnectComplete(int result);
+
+ void LogConnectBegin(const AddressList& addresses);
+ void LogConnectEnd(int net_error);
+
+ int DoRead(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
+ void DidCompleteConnect();
+ void DidCompleteWrite();
+ void DidSignalRead();
+
SOCKET socket_;
- HANDLE socket_event_;
+ HANDLE accept_event_;
base::win::ObjectWatcher accept_watcher_;
scoped_ptr<TCPSocketWin>* accept_socket_;
IPEndPoint* accept_address_;
CompletionCallback accept_callback_;
+ // The various states that the socket could be in.
+ bool waiting_connect_;
+ bool waiting_read_;
+ bool waiting_write_;
+
+ // The core of the socket that can live longer than the socket itself. We pass
+ // resources to the Windows async IO functions and we have to make sure that
+ // they are not destroyed while the OS still references them.
+ scoped_refptr<Core> core_;
+
+ // External callback; called when connect or read is complete.
+ CompletionCallback read_callback_;
+
+ // External callback; called when write is complete.
+ CompletionCallback write_callback_;
+
+ scoped_ptr<IPEndPoint> peer_address_;
+ // The OS error that a connect attempt last completed with.
+ int connect_os_error_;
+
+ bool logging_multiple_connect_attempts_;
+
BoundNetLog net_log_;
DISALLOW_COPY_AND_ASSIGN(TCPSocketWin);
@@ -72,3 +147,4 @@ class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe),
} // namespace net
#endif // NET_SOCKET_TCP_SOCKET_WIN_H_
+
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 88c7e239fa..d03e3e651a 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -101,10 +101,11 @@ LoadState TransportConnectJob::GetLoadState() const {
case STATE_TRANSPORT_CONNECT:
case STATE_TRANSPORT_CONNECT_COMPLETE:
return LOAD_STATE_CONNECTING;
- default:
- NOTREACHED();
+ case STATE_NONE:
return LOAD_STATE_IDLE;
}
+ NOTREACHED();
+ return LOAD_STATE_IDLE;
}
// static
diff --git a/net/ssl/ssl_cipher_suite_names.cc b/net/ssl/ssl_cipher_suite_names.cc
index f12d017fe7..f9394dfd68 100644
--- a/net/ssl/ssl_cipher_suite_names.cc
+++ b/net/ssl/ssl_cipher_suite_names.cc
@@ -194,6 +194,8 @@ static const struct CipherSuite kCipherSuites[] = {
{0xc08b, 0x1087}, // TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
{0xc08c, 0xf7f}, // TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
{0xc08d, 0xf87}, // TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
+ {0xcc13, 0x108f}, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
+ {0xcc14, 0x0d8f}, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
};
static const struct {
@@ -220,8 +222,8 @@ static const struct {
};
static const struct {
- char name[17];
-} kCipherNames[17] = {
+ char name[18];
+} kCipherNames[18] = {
{"NULL"}, // 0
{"RC4_40"}, // 1
{"RC4_128"}, // 2
@@ -239,6 +241,7 @@ static const struct {
{"AES_256_GCM"}, // 14
{"CAMELLIA_128_GCM"}, // 15
{"CAMELLIA_256_GCM"}, // 16
+ {"CHACHA20_POLY1305"}, // 17
};
static const struct {
diff --git a/net/test/python_utils.cc b/net/test/python_utils.cc
index 30c5f679a8..a9ac98b531 100644
--- a/net/test/python_utils.cc
+++ b/net/test/python_utils.cc
@@ -106,18 +106,8 @@ bool GetPyProtoPath(base::FilePath* dir) {
bool GetPythonCommand(CommandLine* python_cmd) {
DCHECK(python_cmd);
- base::FilePath dir;
-#if defined(OS_WIN)
- if (!PathService::Get(base::DIR_SOURCE_ROOT, &dir))
- return false;
- dir = dir.Append(FILE_PATH_LITERAL("third_party"))
- .Append(FILE_PATH_LITERAL("python_26"))
- .Append(FILE_PATH_LITERAL("python.exe"));
-#elif defined(OS_POSIX)
- dir = base::FilePath("python");
-#endif
- python_cmd->SetProgram(dir);
+ python_cmd->SetProgram(base::FilePath(FILE_PATH_LITERAL("python")));
// Launch python in unbuffered mode, so that python output doesn't mix with
// gtest output in buildbot log files. See http://crbug.com/147368.
diff --git a/net/test/spawned_test_server/local_test_server_win.cc b/net/test/spawned_test_server/local_test_server_win.cc
index fd26483ed4..08d68f80bf 100644
--- a/net/test/spawned_test_server/local_test_server_win.cc
+++ b/net/test/spawned_test_server/local_test_server_win.cc
@@ -10,6 +10,7 @@
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
@@ -81,6 +82,60 @@ bool ReadData(HANDLE read_fd, HANDLE write_fd,
return true;
}
+// Class that sets up a temporary path that includes the supplied path
+// at the end.
+//
+// TODO(bratell): By making this more generic we can possibly reuse
+// it at other places such as
+// chrome/common/multi_process_lock_unittest.cc.
+class ScopedPath {
+ public:
+ // Constructor which sets up the environment to include the path to
+ // |path_to_add|.
+ explicit ScopedPath(const base::FilePath& path_to_add);
+
+ // Destructor that restores the path that were active when the
+ // object was constructed.
+ ~ScopedPath();
+
+ private:
+ // The PATH environment variable before it was changed or an empty
+ // string if there was no PATH environment variable.
+ std::string old_path_;
+
+ // The helper object that allows us to read and set environment
+ // variables more easily.
+ scoped_ptr<base::Environment> environment_;
+
+ // A flag saying if we have actually modified the environment.
+ bool path_modified_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPath);
+};
+
+ScopedPath::ScopedPath(const base::FilePath& path_to_add)
+ : environment_(base::Environment::Create()),
+ path_modified_(false) {
+ environment_->GetVar("PATH", &old_path_);
+
+ std::string new_value = old_path_;
+ if (!new_value.empty())
+ new_value += ";";
+
+ new_value += WideToUTF8(path_to_add.value());
+
+ path_modified_ = environment_->SetVar("PATH", new_value);
+}
+
+ScopedPath::~ScopedPath() {
+ if (!path_modified_)
+ return;
+ if (old_path_.empty())
+ environment_->UnSetVar("PATH");
+ else
+ environment_->SetVar("PATH", old_path_);
+}
+
} // namespace
namespace net {
@@ -129,11 +184,21 @@ bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
return false;
}
- if (!base::SetJobObjectAsKillOnJobClose(job_handle_.Get())) {
- LOG(ERROR) << "Could not SetInformationJobObject.";
+ if (!base::SetJobObjectLimitFlags(job_handle_.Get(),
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)) {
+ LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
return false;
}
+ // Add our internal python to the path so it can be used if there is
+ // no system python.
+ base::FilePath python_dir;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_dir)) {
+ LOG(ERROR) << "Could not locate source root directory.";
+ return false;
+ }
+ python_dir = python_dir.AppendASCII("third_party").AppendASCII("python_26");
+ ScopedPath python_path(python_dir);
base::LaunchOptions launch_options;
launch_options.inherit_handles = true;
launch_options.job_handle = job_handle_.Get();
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
index 0bf51b0bdc..73eb259156 100644
--- a/net/third_party/nss/README.chromium
+++ b/net/third_party/nss/README.chromium
@@ -112,6 +112,10 @@ Patches:
client private key is a 1024-bit RSA or DSA key.
patches/tls12backuphash.patch
+ * Support ChaCha20+Poly1305 ciphersuites
+ http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-01
+ patches/chacha20poly1305.patch
+
Apply the patches to NSS by running the patches/applypatches.sh script. Read
the comments at the top of patches/applypatches.sh for instructions.
diff --git a/net/third_party/nss/patches/applypatches.sh b/net/third_party/nss/patches/applypatches.sh
index 2fba203ca0..e936182471 100755
--- a/net/third_party/nss/patches/applypatches.sh
+++ b/net/third_party/nss/patches/applypatches.sh
@@ -53,3 +53,5 @@ patch -p4 < $patches_dir/aesgcm.patch
patch -p4 < $patches_dir/aesgcmchromium.patch
patch -p4 < $patches_dir/tls12backuphash.patch
+
+patch -p4 < $patches_dir/chacha20poly1305.patch
diff --git a/net/third_party/nss/patches/chacha20poly1305.patch b/net/third_party/nss/patches/chacha20poly1305.patch
new file mode 100644
index 0000000000..c858413f3c
--- /dev/null
+++ b/net/third_party/nss/patches/chacha20poly1305.patch
@@ -0,0 +1,280 @@
+diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+index 8be517c..53c29f0 100644
+--- a/nss/lib/ssl/ssl3con.c
++++ b/nss/lib/ssl/ssl3con.c
+@@ -40,6 +40,21 @@
+ #define CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256 (CKM_NSS + 24)
+ #endif
+
++/* This is a bodge to allow this code to be compiled against older NSS
++ * headers. */
++#ifndef CKM_NSS_CHACHA20_POLY1305
++#define CKM_NSS_CHACHA20_POLY1305 (CKM_NSS + 25)
++
++typedef struct CK_AEAD_PARAMS {
++ CK_BYTE_PTR pIv; /* This is the nonce. */
++ CK_ULONG ulIvLen;
++ CK_BYTE_PTR pAAD;
++ CK_ULONG ulAADLen;
++ CK_ULONG ulTagBits;
++} CK_AEAD_PARAMS;
++
++#endif
++
+ #include <stdio.h>
+ #ifdef NSS_ENABLE_ZLIB
+ #include "zlib.h"
+@@ -100,6 +115,8 @@ static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
+ static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
+ /* cipher_suite policy enabled is_present*/
+ #ifdef NSS_ENABLE_ECC
++ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
++ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ #endif /* NSS_ENABLE_ECC */
+@@ -273,6 +290,7 @@ static const ssl3BulkCipherDef bulk_cipher_defs[] = {
+ {cipher_camellia_256, calg_camellia, 32,32, type_block, 16,16, 0, 0},
+ {cipher_seed, calg_seed, 16,16, type_block, 16,16, 0, 0},
+ {cipher_aes_128_gcm, calg_aes_gcm, 16,16, type_aead, 4, 0,16, 8},
++ {cipher_chacha20, calg_chacha20, 32,32, type_aead, 0, 0,16, 0},
+ {cipher_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
+ };
+
+@@ -399,6 +417,8 @@ static const ssl3CipherSuiteDef cipher_suite_defs[] =
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_rsa},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_rsa},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_ecdsa},
++ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, cipher_chacha20, mac_aead, kea_ecdhe_rsa},
++ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, cipher_chacha20, mac_aead, kea_ecdhe_ecdsa},
+
+ #ifdef NSS_ENABLE_ECC
+ {TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_ecdsa},
+@@ -464,6 +484,7 @@ static const SSLCipher2Mech alg2Mech[] = {
+ { calg_camellia , CKM_CAMELLIA_CBC },
+ { calg_seed , CKM_SEED_CBC },
+ { calg_aes_gcm , CKM_AES_GCM },
++ { calg_chacha20 , CKM_NSS_CHACHA20_POLY1305 },
+ /* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
+ };
+
+@@ -2020,6 +2041,46 @@ ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
+ }
+ #endif
+
++static SECStatus
++ssl3_ChaCha20Poly1305(
++ ssl3KeyMaterial *keys,
++ PRBool doDecrypt,
++ unsigned char *out,
++ int *outlen,
++ int maxout,
++ const unsigned char *in,
++ int inlen,
++ const unsigned char *additionalData,
++ int additionalDataLen)
++{
++ SECItem param;
++ SECStatus rv = SECFailure;
++ unsigned int uOutLen;
++ CK_AEAD_PARAMS aeadParams;
++ static const int tagSize = 16;
++
++ param.type = siBuffer;
++ param.len = sizeof(aeadParams);
++ param.data = (unsigned char *) &aeadParams;
++ memset(&aeadParams, 0, sizeof(CK_AEAD_PARAMS));
++ aeadParams.pIv = (unsigned char *) additionalData;
++ aeadParams.ulIvLen = 8;
++ aeadParams.pAAD = (unsigned char *) additionalData;
++ aeadParams.ulAADLen = additionalDataLen;
++ aeadParams.ulTagBits = tagSize * 8;
++
++ if (doDecrypt) {
++ rv = pk11_decrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
++ out, &uOutLen, maxout, in, inlen);
++ } else {
++ rv = pk11_encrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
++ out, &uOutLen, maxout, in, inlen);
++ }
++ *outlen = (int) uOutLen;
++
++ return rv;
++}
++
+ /* Initialize encryption and MAC contexts for pending spec.
+ * Master Secret already is derived.
+ * Caller holds Spec write lock.
+@@ -2053,13 +2114,17 @@ ssl3_InitPendingContextsPKCS11(sslSocket *ss)
+ pwSpec->client.write_mac_context = NULL;
+ pwSpec->server.write_mac_context = NULL;
+
+- if (calg == calg_aes_gcm) {
++ if (calg == calg_aes_gcm || calg == calg_chacha20) {
+ pwSpec->encode = NULL;
+ pwSpec->decode = NULL;
+ pwSpec->destroy = NULL;
+ pwSpec->encodeContext = NULL;
+ pwSpec->decodeContext = NULL;
+- pwSpec->aead = ssl3_AESGCM;
++ if (calg == calg_aes_gcm) {
++ pwSpec->aead = ssl3_AESGCM;
++ } else {
++ pwSpec->aead = ssl3_ChaCha20Poly1305;
++ }
+ return SECSuccess;
+ }
+
+diff --git a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
+index a3638e7..21a5e05 100644
+--- a/nss/lib/ssl/ssl3ecc.c
++++ b/nss/lib/ssl/ssl3ecc.c
+@@ -913,6 +913,7 @@ static const ssl3CipherSuite ecdhe_ecdsa_suites[] = {
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ 0 /* end of list marker */
+@@ -924,6 +925,7 @@ static const ssl3CipherSuite ecdhe_rsa_suites[] = {
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_NULL_SHA,
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ 0 /* end of list marker */
+@@ -936,6 +938,7 @@ static const ssl3CipherSuite ecSuites[] = {
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
++ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+@@ -943,6 +946,7 @@ static const ssl3CipherSuite ecSuites[] = {
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
++ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_NULL_SHA,
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+diff --git a/nss/lib/ssl/sslenum.c b/nss/lib/ssl/sslenum.c
+index 597ec07..fc6b854 100644
+--- a/nss/lib/ssl/sslenum.c
++++ b/nss/lib/ssl/sslenum.c
+@@ -31,6 +31,8 @@
+ const PRUint16 SSL_ImplementedCiphers[] = {
+ /* AES-GCM */
+ #ifdef NSS_ENABLE_ECC
++ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
++ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ #endif /* NSS_ENABLE_ECC */
+diff --git a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h
+index 0fe12d0..e3ae9ce 100644
+--- a/nss/lib/ssl/sslimpl.h
++++ b/nss/lib/ssl/sslimpl.h
+@@ -65,6 +65,7 @@ typedef SSLSignType SSL3SignType;
+ #define calg_camellia ssl_calg_camellia
+ #define calg_seed ssl_calg_seed
+ #define calg_aes_gcm ssl_calg_aes_gcm
++#define calg_chacha20 ssl_calg_chacha20
+
+ #define mac_null ssl_mac_null
+ #define mac_md5 ssl_mac_md5
+@@ -292,7 +293,7 @@ typedef struct {
+ } ssl3CipherSuiteCfg;
+
+ #ifdef NSS_ENABLE_ECC
+-#define ssl_V3_SUITES_IMPLEMENTED 61
++#define ssl_V3_SUITES_IMPLEMENTED 63
+ #else
+ #define ssl_V3_SUITES_IMPLEMENTED 37
+ #endif /* NSS_ENABLE_ECC */
+@@ -474,6 +475,7 @@ typedef enum {
+ cipher_camellia_256,
+ cipher_seed,
+ cipher_aes_128_gcm,
++ cipher_chacha20,
+ cipher_missing /* reserved for no such supported cipher */
+ /* This enum must match ssl3_cipherName[] in ssl3con.c. */
+ } SSL3BulkCipher;
+diff --git a/nss/lib/ssl/sslinfo.c b/nss/lib/ssl/sslinfo.c
+index 9597209..bfc1676 100644
+--- a/nss/lib/ssl/sslinfo.c
++++ b/nss/lib/ssl/sslinfo.c
+@@ -118,6 +118,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
+ #define C_NULL "NULL", calg_null
+ #define C_SJ "SKIPJACK", calg_sj
+ #define C_AESGCM "AES-GCM", calg_aes_gcm
++#define C_CHACHA20 "CHACHA20POLY1305", calg_chacha20
+
+ #define B_256 256, 256, 256
+ #define B_128 128, 128, 128
+@@ -196,12 +197,14 @@ static const SSLCipherSuiteInfo suiteInfo[] = {
+ {0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, 1, 0, 0, },
+ {0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, 1, 0, 0, },
+ {0,CS(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, 1, 0, 0, },
++{0,CS(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305),S_ECDSA,K_ECDHE,C_CHACHA20,B_256,M_AEAD_128,0, 0, 0, },
+
+ {0,CS(TLS_ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, 0, 0, 0, },
+ {0,CS(TLS_ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, 0, 0, 0, },
+ {0,CS(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, 1, 0, 0, },
+ {0,CS(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, 1, 0, 0, },
+ {0,CS(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, 1, 0, 0, },
++{0,CS(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305), S_RSA,K_ECDHE,C_CHACHA20,B_256,M_AEAD_128, 0, 0, 0, },
+
+ {0,CS(TLS_ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, 0, 0, 0, },
+ {0,CS(TLS_ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, 0, 0, 0, },
+diff --git a/nss/lib/ssl/sslproto.h b/nss/lib/ssl/sslproto.h
+index 53bba01..6b60a28 100644
+--- a/nss/lib/ssl/sslproto.h
++++ b/nss/lib/ssl/sslproto.h
+@@ -213,6 +213,9 @@
+ #define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
+ #define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031
+
++#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 0xCC13
++#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 0xCC14
++
+ /* Netscape "experimental" cipher suites. */
+ #define SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA 0xffe0
+ #define SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA 0xffe1
+diff --git a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c
+index c17c7a3..ffbccc6 100644
+--- a/nss/lib/ssl/sslsock.c
++++ b/nss/lib/ssl/sslsock.c
+@@ -98,6 +98,7 @@ static cipherPolicy ssl_ciphers[] = { /* Export France */
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
++ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDH_RSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
+ { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+@@ -110,6 +111,7 @@ static cipherPolicy ssl_ciphers[] = { /* Export France */
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
++ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ #endif /* NSS_ENABLE_ECC */
+ { 0, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED }
+ };
+diff --git a/nss/lib/ssl/sslt.h b/nss/lib/ssl/sslt.h
+index b03422e..a8007d8 100644
+--- a/nss/lib/ssl/sslt.h
++++ b/nss/lib/ssl/sslt.h
+@@ -94,7 +94,8 @@ typedef enum {
+ ssl_calg_aes = 7,
+ ssl_calg_camellia = 8,
+ ssl_calg_seed = 9,
+- ssl_calg_aes_gcm = 10
++ ssl_calg_aes_gcm = 10,
++ ssl_calg_chacha20 = 11
+ } SSLCipherAlgorithm;
+
+ typedef enum {
diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
index 8be517cde6..53c29f0e28 100644
--- a/net/third_party/nss/ssl/ssl3con.c
+++ b/net/third_party/nss/ssl/ssl3con.c
@@ -40,6 +40,21 @@
#define CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256 (CKM_NSS + 24)
#endif
+/* This is a bodge to allow this code to be compiled against older NSS
+ * headers. */
+#ifndef CKM_NSS_CHACHA20_POLY1305
+#define CKM_NSS_CHACHA20_POLY1305 (CKM_NSS + 25)
+
+typedef struct CK_AEAD_PARAMS {
+ CK_BYTE_PTR pIv; /* This is the nonce. */
+ CK_ULONG ulIvLen;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulTagBits;
+} CK_AEAD_PARAMS;
+
+#endif
+
#include <stdio.h>
#ifdef NSS_ENABLE_ZLIB
#include "zlib.h"
@@ -100,6 +115,8 @@ static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
/* cipher_suite policy enabled is_present*/
#ifdef NSS_ENABLE_ECC
+ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
{ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
#endif /* NSS_ENABLE_ECC */
@@ -273,6 +290,7 @@ static const ssl3BulkCipherDef bulk_cipher_defs[] = {
{cipher_camellia_256, calg_camellia, 32,32, type_block, 16,16, 0, 0},
{cipher_seed, calg_seed, 16,16, type_block, 16,16, 0, 0},
{cipher_aes_128_gcm, calg_aes_gcm, 16,16, type_aead, 4, 0,16, 8},
+ {cipher_chacha20, calg_chacha20, 32,32, type_aead, 0, 0,16, 0},
{cipher_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
};
@@ -399,6 +417,8 @@ static const ssl3CipherSuiteDef cipher_suite_defs[] =
{TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_rsa},
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_rsa},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_ecdsa},
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, cipher_chacha20, mac_aead, kea_ecdhe_rsa},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, cipher_chacha20, mac_aead, kea_ecdhe_ecdsa},
#ifdef NSS_ENABLE_ECC
{TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_ecdsa},
@@ -464,6 +484,7 @@ static const SSLCipher2Mech alg2Mech[] = {
{ calg_camellia , CKM_CAMELLIA_CBC },
{ calg_seed , CKM_SEED_CBC },
{ calg_aes_gcm , CKM_AES_GCM },
+ { calg_chacha20 , CKM_NSS_CHACHA20_POLY1305 },
/* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
};
@@ -2020,6 +2041,46 @@ ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
}
#endif
+static SECStatus
+ssl3_ChaCha20Poly1305(
+ ssl3KeyMaterial *keys,
+ PRBool doDecrypt,
+ unsigned char *out,
+ int *outlen,
+ int maxout,
+ const unsigned char *in,
+ int inlen,
+ const unsigned char *additionalData,
+ int additionalDataLen)
+{
+ SECItem param;
+ SECStatus rv = SECFailure;
+ unsigned int uOutLen;
+ CK_AEAD_PARAMS aeadParams;
+ static const int tagSize = 16;
+
+ param.type = siBuffer;
+ param.len = sizeof(aeadParams);
+ param.data = (unsigned char *) &aeadParams;
+ memset(&aeadParams, 0, sizeof(CK_AEAD_PARAMS));
+ aeadParams.pIv = (unsigned char *) additionalData;
+ aeadParams.ulIvLen = 8;
+ aeadParams.pAAD = (unsigned char *) additionalData;
+ aeadParams.ulAADLen = additionalDataLen;
+ aeadParams.ulTagBits = tagSize * 8;
+
+ if (doDecrypt) {
+ rv = pk11_decrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
+ out, &uOutLen, maxout, in, inlen);
+ } else {
+ rv = pk11_encrypt(keys->write_key, CKM_NSS_CHACHA20_POLY1305, &param,
+ out, &uOutLen, maxout, in, inlen);
+ }
+ *outlen = (int) uOutLen;
+
+ return rv;
+}
+
/* Initialize encryption and MAC contexts for pending spec.
* Master Secret already is derived.
* Caller holds Spec write lock.
@@ -2053,13 +2114,17 @@ ssl3_InitPendingContextsPKCS11(sslSocket *ss)
pwSpec->client.write_mac_context = NULL;
pwSpec->server.write_mac_context = NULL;
- if (calg == calg_aes_gcm) {
+ if (calg == calg_aes_gcm || calg == calg_chacha20) {
pwSpec->encode = NULL;
pwSpec->decode = NULL;
pwSpec->destroy = NULL;
pwSpec->encodeContext = NULL;
pwSpec->decodeContext = NULL;
- pwSpec->aead = ssl3_AESGCM;
+ if (calg == calg_aes_gcm) {
+ pwSpec->aead = ssl3_AESGCM;
+ } else {
+ pwSpec->aead = ssl3_ChaCha20Poly1305;
+ }
return SECSuccess;
}
diff --git a/net/third_party/nss/ssl/ssl3ecc.c b/net/third_party/nss/ssl/ssl3ecc.c
index a3638e75f1..21a5e05cf2 100644
--- a/net/third_party/nss/ssl/ssl3ecc.c
+++ b/net/third_party/nss/ssl/ssl3ecc.c
@@ -913,6 +913,7 @@ static const ssl3CipherSuite ecdhe_ecdsa_suites[] = {
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_NULL_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
0 /* end of list marker */
@@ -924,6 +925,7 @@ static const ssl3CipherSuite ecdhe_rsa_suites[] = {
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_NULL_SHA,
TLS_ECDHE_RSA_WITH_RC4_128_SHA,
0 /* end of list marker */
@@ -936,6 +938,7 @@ static const ssl3CipherSuite ecSuites[] = {
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_NULL_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
@@ -943,6 +946,7 @@ static const ssl3CipherSuite ecSuites[] = {
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_RSA_WITH_NULL_SHA,
TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
diff --git a/net/third_party/nss/ssl/sslenum.c b/net/third_party/nss/ssl/sslenum.c
index 597ec07239..fc6b85423b 100644
--- a/net/third_party/nss/ssl/sslenum.c
+++ b/net/third_party/nss/ssl/sslenum.c
@@ -31,6 +31,8 @@
const PRUint16 SSL_ImplementedCiphers[] = {
/* AES-GCM */
#ifdef NSS_ENABLE_ECC
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
#endif /* NSS_ENABLE_ECC */
diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h
index 0fe12d0d7e..e3ae9cec5d 100644
--- a/net/third_party/nss/ssl/sslimpl.h
+++ b/net/third_party/nss/ssl/sslimpl.h
@@ -65,6 +65,7 @@ typedef SSLSignType SSL3SignType;
#define calg_camellia ssl_calg_camellia
#define calg_seed ssl_calg_seed
#define calg_aes_gcm ssl_calg_aes_gcm
+#define calg_chacha20 ssl_calg_chacha20
#define mac_null ssl_mac_null
#define mac_md5 ssl_mac_md5
@@ -292,7 +293,7 @@ typedef struct {
} ssl3CipherSuiteCfg;
#ifdef NSS_ENABLE_ECC
-#define ssl_V3_SUITES_IMPLEMENTED 61
+#define ssl_V3_SUITES_IMPLEMENTED 63
#else
#define ssl_V3_SUITES_IMPLEMENTED 37
#endif /* NSS_ENABLE_ECC */
@@ -474,6 +475,7 @@ typedef enum {
cipher_camellia_256,
cipher_seed,
cipher_aes_128_gcm,
+ cipher_chacha20,
cipher_missing /* reserved for no such supported cipher */
/* This enum must match ssl3_cipherName[] in ssl3con.c. */
} SSL3BulkCipher;
diff --git a/net/third_party/nss/ssl/sslinfo.c b/net/third_party/nss/ssl/sslinfo.c
index 959720948d..bfc16767ff 100644
--- a/net/third_party/nss/ssl/sslinfo.c
+++ b/net/third_party/nss/ssl/sslinfo.c
@@ -118,6 +118,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
#define C_NULL "NULL", calg_null
#define C_SJ "SKIPJACK", calg_sj
#define C_AESGCM "AES-GCM", calg_aes_gcm
+#define C_CHACHA20 "CHACHA20POLY1305", calg_chacha20
#define B_256 256, 256, 256
#define B_128 128, 128, 128
@@ -196,12 +197,14 @@ static const SSLCipherSuiteInfo suiteInfo[] = {
{0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA, 1, 0, 0, },
{0,CS(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256), S_ECDSA, K_ECDHE, C_AES, B_128, M_SHA256, 1, 0, 0, },
{0,CS(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), S_ECDSA, K_ECDHE, C_AES, B_256, M_SHA, 1, 0, 0, },
+{0,CS(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305),S_ECDSA,K_ECDHE,C_CHACHA20,B_256,M_AEAD_128,0, 0, 0, },
{0,CS(TLS_ECDH_RSA_WITH_NULL_SHA), S_RSA, K_ECDH, C_NULL, B_0, M_SHA, 0, 0, 0, },
{0,CS(TLS_ECDH_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDH, C_RC4, B_128, M_SHA, 0, 0, 0, },
{0,CS(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA), S_RSA, K_ECDH, C_3DES, B_3DES, M_SHA, 1, 0, 0, },
{0,CS(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA), S_RSA, K_ECDH, C_AES, B_128, M_SHA, 1, 0, 0, },
{0,CS(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA), S_RSA, K_ECDH, C_AES, B_256, M_SHA, 1, 0, 0, },
+{0,CS(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305), S_RSA,K_ECDHE,C_CHACHA20,B_256,M_AEAD_128, 0, 0, 0, },
{0,CS(TLS_ECDHE_RSA_WITH_NULL_SHA), S_RSA, K_ECDHE, C_NULL, B_0, M_SHA, 0, 0, 0, },
{0,CS(TLS_ECDHE_RSA_WITH_RC4_128_SHA), S_RSA, K_ECDHE, C_RC4, B_128, M_SHA, 0, 0, 0, },
diff --git a/net/third_party/nss/ssl/sslproto.h b/net/third_party/nss/ssl/sslproto.h
index 53bba011bb..6b60a28616 100644
--- a/net/third_party/nss/ssl/sslproto.h
+++ b/net/third_party/nss/ssl/sslproto.h
@@ -213,6 +213,9 @@
#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031
+#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 0xCC13
+#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 0xCC14
+
/* Netscape "experimental" cipher suites. */
#define SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA 0xffe0
#define SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA 0xffe1
diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
index c17c7a3ad0..ffbccc6eb6 100644
--- a/net/third_party/nss/ssl/sslsock.c
+++ b/net/third_party/nss/ssl/sslsock.c
@@ -98,6 +98,7 @@ static cipherPolicy ssl_ciphers[] = { /* Export France */
{ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDH_RSA_WITH_NULL_SHA, SSL_ALLOWED, SSL_ALLOWED },
{ TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
@@ -110,6 +111,7 @@ static cipherPolicy ssl_ciphers[] = { /* Export France */
{ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
{ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
+ { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED },
#endif /* NSS_ENABLE_ECC */
{ 0, SSL_NOT_ALLOWED, SSL_NOT_ALLOWED }
};
diff --git a/net/third_party/nss/ssl/sslt.h b/net/third_party/nss/ssl/sslt.h
index b03422eaa5..a8007d8b4c 100644
--- a/net/third_party/nss/ssl/sslt.h
+++ b/net/third_party/nss/ssl/sslt.h
@@ -94,7 +94,8 @@ typedef enum {
ssl_calg_aes = 7,
ssl_calg_camellia = 8,
ssl_calg_seed = 9,
- ssl_calg_aes_gcm = 10
+ ssl_calg_aes_gcm = 10,
+ ssl_calg_chacha20 = 11
} SSLCipherAlgorithm;
typedef enum {
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 9ad6649be9..7b61be6d14 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -423,7 +423,29 @@ TEST_P(EndToEndTest, PostMissingBytes) {
EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
}
-TEST_P(EndToEndTest, LargePost) {
+TEST_P(EndToEndTest, LargePostNoPacketLoss) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+
+ // 1 Mb body.
+ string body;
+ GenerateBody(&body, 1024 * 1024);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+}
+
+TEST_P(EndToEndTest, LargePostWithPacketLoss) {
// TODO(rtenneti): Delete this when NSS is supported.
if (!Aes128Gcm12Encrypter::IsSupported()) {
LOG(INFO) << "AES GCM not supported. Test skipped.";
@@ -439,8 +461,9 @@ TEST_P(EndToEndTest, LargePost) {
client_->client()->WaitForCryptoHandshakeConfirmed();
// FLAGS_fake_packet_loss_percentage = 30;
+ // 10 Kb body.
string body;
- GenerateBody(&body, 10240);
+ GenerateBody(&body, 1024 * 10);
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
@@ -563,7 +586,6 @@ TEST_P(EndToEndTest, DISABLED_MultipleTermination) {
}
ASSERT_TRUE(Initialize());
- scoped_ptr<QuicTestClient> client2(CreateQuicClient());
HTTPMessage request(HttpConstants::HTTP_1_1,
HttpConstants::POST, "/foo");
diff --git a/net/tools/quic/quic_epoll_connection_helper_test.cc b/net/tools/quic/quic_epoll_connection_helper_test.cc
index 6d6ea13d01..636d98f7ee 100644
--- a/net/tools/quic/quic_epoll_connection_helper_test.cc
+++ b/net/tools/quic/quic_epoll_connection_helper_test.cc
@@ -8,6 +8,7 @@
#include "net/quic/crypto/quic_decrypter.h"
#include "net/quic/crypto/quic_encrypter.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_connection.h"
#include "net/quic/quic_framer.h"
#include "net/quic/test_tools/quic_connection_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
@@ -21,6 +22,7 @@ using net::test::QuicConnectionPeer;
using net::test::MockConnectionVisitor;
using net::tools::test::MockEpollServer;
using testing::_;
+using testing::AnyNumber;
using testing::Return;
namespace net {
@@ -91,6 +93,8 @@ class QuicEpollConnectionHelperTest : public ::testing::Test {
QuicBandwidth::FromKBitsPerSecond(100)));
EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(Return(
QuicTime::Delta::FromMilliseconds(100)));
+ ON_CALL(*send_algorithm_, SentPacket(_, _, _, _, _))
+ .WillByDefault(Return(true));
}
QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
@@ -136,12 +140,14 @@ TEST_F(QuicEpollConnectionHelperTest, DISABLED_TestRetransmission) {
arraysize(buffer) - 1;
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, 1, packet_size, NOT_RETRANSMISSION));
+ SentPacket(_, 1, packet_size, NOT_RETRANSMISSION, _));
EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, packet_size));
- connection_.SendStreamData(1, buffer, 0, false);
+ struct iovec iov = {const_cast<char*>(buffer),
+ static_cast<size_t>(3)};
+ connection_.SendvStreamData(1, &iov, 1, 0, false);
EXPECT_EQ(1u, helper_->header()->packet_sequence_number);
EXPECT_CALL(*send_algorithm_,
- SentPacket(_, 2, packet_size, IS_RETRANSMISSION));
+ SentPacket(_, 2, packet_size, IS_RETRANSMISSION, _));
epoll_server_.AdvanceByAndCallCallbacks(kDefaultRetransmissionTimeMs * 1000);
EXPECT_EQ(2u, helper_->header()->packet_sequence_number);
@@ -150,7 +156,10 @@ TEST_F(QuicEpollConnectionHelperTest, DISABLED_TestRetransmission) {
TEST_F(QuicEpollConnectionHelperTest, InitialTimeout) {
EXPECT_TRUE(connection_.connected());
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
epoll_server_.WaitForEventsAndExecuteCallbacks();
EXPECT_FALSE(connection_.connected());
@@ -167,7 +176,8 @@ TEST_F(QuicEpollConnectionHelperTest, TimeoutAfterSend) {
EXPECT_EQ(5000, epoll_server_.NowInUsec());
// Send an ack so we don't set the retransmission alarm.
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_,
+ SentPacket(_, 1, _, NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA));
connection_.SendAck();
// The original alarm will fire. We should not time out because we had a
@@ -177,7 +187,10 @@ TEST_F(QuicEpollConnectionHelperTest, TimeoutAfterSend) {
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillOnce(
+ Return(QuicTime::Delta::FromMicroseconds(1)));
epoll_server_.WaitForEventsAndExecuteCallbacks();
EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
epoll_server_.NowInUsec());
@@ -191,17 +204,21 @@ TEST_F(QuicEpollConnectionHelperTest, SendSchedulerDelayThenSend) {
QuicPacket* packet = ConstructDataPacket(1, 0);
EXPECT_CALL(
*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
- testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ Return(QuicTime::Delta::FromMicroseconds(1)));
connection_.SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0,
- HAS_RETRANSMITTABLE_DATA);
- EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ HAS_RETRANSMITTABLE_DATA,
+ QuicConnection::NO_FORCE);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION,
+ _));
EXPECT_EQ(1u, connection_.NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
- EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).
- WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
- EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
epoll_server_.AdvanceByAndCallCallbacks(1);
EXPECT_EQ(0u, connection_.NumQueuedPackets());
}
diff --git a/net/tools/quic/quic_reliable_server_stream_test.cc b/net/tools/quic/quic_reliable_server_stream_test.cc
index b946d94cfc..53533a35cb 100644
--- a/net/tools/quic/quic_reliable_server_stream_test.cc
+++ b/net/tools/quic/quic_reliable_server_stream_test.cc
@@ -58,7 +58,9 @@ class QuicReliableServerStreamTest : public ::testing::Test {
stream_.reset(new QuicSpdyServerStream(3, &session_));
}
- QuicConsumedData ValidateHeaders(StringPiece headers) {
+ QuicConsumedData ValidateHeaders(const struct iovec* iov) {
+ StringPiece headers =
+ StringPiece(static_cast<const char*>(iov[0].iov_base), iov[0].iov_len);
headers_string_ = SpdyUtils::SerializeResponseHeaders(
response_headers_);
QuicSpdyDecompressor decompressor;
@@ -119,13 +121,20 @@ class QuicReliableServerStreamTest : public ::testing::Test {
string body_;
};
-QuicConsumedData ConsumeAllData(QuicStreamId id, StringPiece data,
- QuicStreamOffset offset, bool fin) {
- return QuicConsumedData(data.size(), fin);
+QuicConsumedData ConsumeAllData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ QuicStreamOffset offset,
+ bool fin) {
+ ssize_t consumed_length = 0;
+ for (int i = 0; i < iov_count; ++i) {
+ consumed_length += iov[i].iov_len;
+ }
+ return QuicConsumedData(consumed_length, fin);
}
TEST_F(QuicReliableServerStreamTest, TestFraming) {
- EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(AnyNumber()).
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(AnyNumber()).
WillRepeatedly(Invoke(ConsumeAllData));
EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
@@ -138,7 +147,7 @@ TEST_F(QuicReliableServerStreamTest, TestFraming) {
}
TEST_F(QuicReliableServerStreamTest, TestFramingOnePacket) {
- EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(AnyNumber()).
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(AnyNumber()).
WillRepeatedly(Invoke(ConsumeAllData));
string message = headers_string_ + body_;
@@ -156,7 +165,7 @@ TEST_F(QuicReliableServerStreamTest, TestFramingExtraData) {
string large_body = "hello world!!!!!!";
// We'll automatically write out an error (headers + body)
- EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(2).
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(2).
WillRepeatedly(Invoke(ConsumeAllData));
EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
@@ -183,11 +192,11 @@ TEST_F(QuicReliableServerStreamTest, TestSendResponse) {
response_headers_.ReplaceOrAppendHeader("content-length", "3");
InSequence s;
- EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(1)
+ EXPECT_CALL(session_, WritevData(_, _, 1, _, _)).Times(1)
.WillOnce(WithArgs<1>(Invoke(
this, &QuicReliableServerStreamTest::ValidateHeaders)));
- StringPiece kBody = "Yum";
- EXPECT_CALL(session_, WriteData(_, kBody, _, _)).Times(1).
+
+ EXPECT_CALL(session_, WritevData(_, _, 1, _, _)).Times(1).
WillOnce(Return(QuicConsumedData(3, true)));
stream_->SendResponse();
@@ -201,11 +210,11 @@ TEST_F(QuicReliableServerStreamTest, TestSendErrorResponse) {
response_headers_.ReplaceOrAppendHeader("content-length", "3");
InSequence s;
- EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(1)
+ EXPECT_CALL(session_, WritevData(_, _, 1, _, _)).Times(1)
.WillOnce(WithArgs<1>(Invoke(
this, &QuicReliableServerStreamTest::ValidateHeaders)));
- StringPiece kBody = "bad";
- EXPECT_CALL(session_, WriteData(_, kBody, _, _)).Times(1).
+
+ EXPECT_CALL(session_, WritevData(_, _, 1, _, _)).Times(1).
WillOnce(Return(QuicConsumedData(3, true)));
stream_->SendErrorResponse();
diff --git a/net/tools/quic/quic_server_session.h b/net/tools/quic/quic_server_session.h
index 604b9fc0c4..2f031373bd 100644
--- a/net/tools/quic/quic_server_session.h
+++ b/net/tools/quic/quic_server_session.h
@@ -25,6 +25,10 @@ class ReliableQuicStream;
namespace tools {
+namespace test {
+class QuicServerSessionPeer;
+} // namespace test
+
// An interface from the session to the entity owning the session.
// This lets the session notify its owner (the Dispatcher) when the connection
// is closed.
@@ -66,6 +70,8 @@ class QuicServerSession : public QuicSession {
const QuicCryptoServerConfig& crypto_config);
private:
+ friend class test::QuicServerSessionPeer;
+
scoped_ptr<QuicCryptoServerStream> crypto_stream_;
QuicSessionOwner* owner_;
diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc
new file mode 100644
index 0000000000..a6f94abd7b
--- /dev/null
+++ b/net/tools/quic/quic_server_session_test.cc
@@ -0,0 +1,255 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_server_session.h"
+
+
+#include "net/quic/crypto/crypto_server_config.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using __gnu_cxx::vector;
+using net::test::MockConnection;
+using net::test::QuicConnectionPeer;
+using net::test::ReliableQuicStreamPeer;
+using testing::_;
+using testing::StrictMock;
+
+namespace net {
+namespace tools {
+namespace test {
+
+class QuicServerSessionPeer {
+ public:
+ static ReliableQuicStream* GetIncomingReliableStream(
+ QuicServerSession* s, QuicStreamId id) {
+ return s->GetIncomingReliableStream(id);
+ }
+ static ReliableQuicStream* GetStream(QuicServerSession* s, QuicStreamId id) {
+ return s->GetStream(id);
+ }
+};
+
+class CloseOnDataStream : public ReliableQuicStream {
+ public:
+ CloseOnDataStream(QuicStreamId id, QuicSession* session)
+ : ReliableQuicStream(id, session) {
+ }
+
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {
+ session()->MarkDecompressionBlocked(1, id());
+ session()->CloseStream(id());
+ return true;
+ }
+
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE {
+ return 0;
+ }
+};
+
+class TestQuicQuicServerSession : public QuicServerSession {
+ public:
+ TestQuicQuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSessionOwner* owner)
+ : QuicServerSession(config, connection, owner),
+ close_stream_on_data_(false) {
+ }
+
+ virtual ReliableQuicStream* CreateIncomingReliableStream(
+ QuicStreamId id) OVERRIDE {
+ if (!ShouldCreateIncomingReliableStream(id)) {
+ return NULL;
+ }
+ if (close_stream_on_data_) {
+ return new CloseOnDataStream(id, this);
+ } else {
+ return new QuicSpdyServerStream(id, this);
+ }
+ }
+
+ void CloseStreamOnData() {
+ close_stream_on_data_ = true;
+ }
+
+ private:
+ bool close_stream_on_data_;
+};
+
+namespace {
+
+class QuicServerSessionTest : public ::testing::Test {
+ protected:
+ QuicServerSessionTest()
+ : guid_(1),
+ crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance()) {
+ config_.SetDefaults();
+ config_.set_max_streams_per_connection(3, 3);
+
+ connection_ = new MockConnection(guid_, IPEndPoint(), 0, &eps_, true);
+ session_.reset(new TestQuicQuicServerSession(
+ config_, connection_, &owner_));
+ session_->InitializeSession(crypto_config_);
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+ }
+
+ void MarkHeadersReadForStream(QuicStreamId id) {
+ ReliableQuicStream* stream = QuicServerSessionPeer::GetStream(
+ session_.get(), id);
+ ASSERT_TRUE(stream != NULL);
+ ReliableQuicStreamPeer::SetHeadersDecompressed(stream, true);
+ }
+
+ QuicGuid guid_;
+ EpollServer eps_;
+ StrictMock<MockQuicSessionOwner> owner_;
+ MockConnection* connection_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ scoped_ptr<TestQuicQuicServerSession> session_;
+ QuicConnectionVisitorInterface* visitor_;
+};
+
+TEST_F(QuicServerSessionTest, CloseStreamDueToReset) {
+ // Open a stream, then reset it.
+ // Send two bytes of payload to open it.
+ QuicPacketHeader header;
+ header.public_header.guid = guid_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ QuicStreamFrame data1(3, false, 0, "HT");
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+ EXPECT_EQ(1u, session_->GetNumOpenStreams());
+
+ // Pretend we got full headers, so we won't trigger the 'unrecoverable
+ // compression context' state.
+ MarkHeadersReadForStream(3);
+
+ // Send a reset.
+ QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
+ visitor_->OnRstStream(rst1);
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+
+ // Send the same two bytes of payload in a new packet.
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+
+ // The stream should not be re-opened.
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+}
+
+TEST_F(QuicServerSessionTest, NeverOpenStreamDueToReset) {
+ // Send a reset.
+ QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR);
+ visitor_->OnRstStream(rst1);
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+
+ // Send two bytes of payload.
+ QuicPacketHeader header;
+ header.public_header.guid = guid_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ QuicStreamFrame data1(3, false, 0, "HT");
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+
+ // When we get data for the closed stream, it implies the far side has
+ // compressed some headers. As a result we're going to bail due to
+ // unrecoverable compression context state.
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
+ EXPECT_FALSE(visitor_->OnStreamFrames(frames));
+
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+}
+
+TEST_F(QuicServerSessionTest, GoOverPrematureClosedStreamLimit) {
+ QuicPacketHeader header;
+ header.public_header.guid = guid_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ QuicStreamFrame data1(3, false, 0, "H");
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+
+ // Set up the stream such that it's open in OnPacket, but closes half way
+ // through while on the decompression blocked list.
+ session_->CloseStreamOnData();
+
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED));
+ EXPECT_FALSE(visitor_->OnStreamFrames(frames));
+}
+
+TEST_F(QuicServerSessionTest, AcceptClosedStream) {
+ QuicPacketHeader header;
+ header.public_header.guid = guid_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ vector<QuicStreamFrame> frames;
+ // Send (empty) compressed headers followed by two bytes of data.
+ frames.push_back(QuicStreamFrame(3, false, 0, "\1\0\0\0\0\0\0\0HT"));
+ frames.push_back(QuicStreamFrame(5, false, 0, "\2\0\0\0\0\0\0\0HT"));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+
+ // Pretend we got full headers, so we won't trigger the 'unercoverable
+ // compression context' state.
+ MarkHeadersReadForStream(3);
+
+ // Send a reset.
+ QuicRstStreamFrame rst(3, QUIC_STREAM_NO_ERROR);
+ visitor_->OnRstStream(rst);
+
+ // If we were tracking, we'd probably want to reject this because it's data
+ // past the reset point of stream 3. As it's a closed stream we just drop the
+ // data on the floor, but accept the packet because it has data for stream 5.
+ frames.clear();
+ frames.push_back(QuicStreamFrame(3, false, 2, "TP"));
+ frames.push_back(QuicStreamFrame(5, false, 2, "TP"));
+ EXPECT_TRUE(visitor_->OnStreamFrames(frames));
+}
+
+TEST_F(QuicServerSessionTest, MaxNumConnections) {
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+ EXPECT_TRUE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 3));
+ EXPECT_TRUE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 5));
+ EXPECT_TRUE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 7));
+ EXPECT_FALSE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 9));
+}
+
+TEST_F(QuicServerSessionTest, MaxNumConnectionsImplicit) {
+ EXPECT_EQ(0u, session_->GetNumOpenStreams());
+ EXPECT_TRUE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 3));
+ // Implicitly opens two more streams before 9.
+ EXPECT_FALSE(
+ QuicServerSessionPeer::GetIncomingReliableStream(session_.get(), 9));
+}
+
+TEST_F(QuicServerSessionTest, GetEvenIncomingError) {
+ // Incoming streams on the server session must be odd.
+ EXPECT_EQ(NULL,
+ QuicServerSessionPeer::GetIncomingReliableStream(
+ session_.get(), 2));
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/net/tools/quic/quic_spdy_client_stream.h b/net/tools/quic/quic_spdy_client_stream.h
index ec4d25747f..5d32b30d3c 100644
--- a/net/tools/quic/quic_spdy_client_stream.h
+++ b/net/tools/quic/quic_spdy_client_stream.h
@@ -34,6 +34,10 @@ class QuicSpdyClientStream : public QuicReliableClientStream {
base::StringPiece body,
bool fin) OVERRIDE;
+ // While the server's set_priority shouldn't be called externally, the creator
+ // of client-side streams should be able to set the priority.
+ using QuicReliableClientStream::set_priority;
+
private:
int ParseResponseHeaders();
diff --git a/net/tools/quic/quic_time_wait_list_manager_test.cc b/net/tools/quic/quic_time_wait_list_manager_test.cc
new file mode 100644
index 0000000000..8f59687437
--- /dev/null
+++ b/net/tools/quic/quic_time_wait_list_manager_test.cc
@@ -0,0 +1,397 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/tools/quic/quic_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/quic_packet_writer.h"
+#include "net/tools/quic/test_tools/mock_epoll_server.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::FramerVisitorCapturingPublicReset;
+using testing::_;
+using testing::Args;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Truly;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+class TestTimeWaitListManager : public QuicTimeWaitListManager {
+ public:
+ TestTimeWaitListManager(QuicPacketWriter* writer,
+ EpollServer* epoll_server)
+ : QuicTimeWaitListManager(writer, epoll_server) {
+ }
+
+ using QuicTimeWaitListManager::is_write_blocked;
+ using QuicTimeWaitListManager::time_wait_period;
+ using QuicTimeWaitListManager::ShouldSendPublicReset;
+ using QuicTimeWaitListManager::GetQuicVersionFromGuid;
+};
+
+class MockFakeTimeEpollServer : public FakeTimeEpollServer {
+ public:
+ MOCK_METHOD2(RegisterAlarm, void(int64 timeout_in_us,
+ EpollAlarmCallbackInterface* alarm));
+};
+
+class QuicTimeWaitListManagerTest : public testing::Test {
+ protected:
+ QuicTimeWaitListManagerTest()
+ : time_wait_list_manager_(&writer_, &epoll_server_),
+ framer_(QuicVersionMax(),
+ QuicTime::Zero(),
+ true),
+ guid_(45) {
+ }
+
+ void AddGuid(QuicGuid guid) {
+ time_wait_list_manager_.AddGuidToTimeWait(guid, QuicVersionMax());
+ }
+
+ void AddGuid(QuicGuid guid, QuicVersion version) {
+ time_wait_list_manager_.AddGuidToTimeWait(guid, version);
+ }
+
+ bool IsGuidInTimeWait(QuicGuid guid) {
+ return time_wait_list_manager_.IsGuidInTimeWait(guid);
+ }
+
+ void ProcessPacket(QuicGuid guid, const QuicEncryptedPacket& packet) {
+ time_wait_list_manager_.ProcessPacket(server_address_,
+ client_address_,
+ guid,
+ packet);
+ }
+
+ QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicGuid guid,
+ QuicPacketSequenceNumber sequence_number) {
+ QuicPacketHeader header;
+ header.public_header.guid = guid;
+ header.public_header.guid_length = PACKET_8BYTE_GUID;
+ header.public_header.version_flag = false;
+ header.public_header.reset_flag = false;
+ header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.entropy_hash = 0;
+ header.fec_flag = false;
+ header.is_in_fec_group = NOT_IN_FEC_GROUP;
+ header.fec_group = 0;
+ QuicStreamFrame stream_frame(1, false, 0, "data");
+ QuicFrame frame(&stream_frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ framer_.BuildUnsizedDataPacket(header, frames).packet);
+ EXPECT_TRUE(packet != NULL);
+ QuicEncryptedPacket* encrypted = framer_.EncryptPacket(ENCRYPTION_NONE,
+ sequence_number,
+ *packet);
+ EXPECT_TRUE(encrypted != NULL);
+ return encrypted;
+ }
+
+ MockFakeTimeEpollServer epoll_server_;
+ MockPacketWriter writer_;
+ TestTimeWaitListManager time_wait_list_manager_;
+ QuicFramer framer_;
+ QuicGuid guid_;
+ IPEndPoint server_address_;
+ IPEndPoint client_address_;
+};
+
+class ValidatePublicResetPacketPredicate
+ : public MatcherInterface<const std::tr1::tuple<const char*, int> > {
+ public:
+ explicit ValidatePublicResetPacketPredicate(QuicGuid guid,
+ QuicPacketSequenceNumber number)
+ : guid_(guid), sequence_number_(number) {
+ }
+
+ virtual bool MatchAndExplain(
+ const std::tr1::tuple<const char*, int> packet_buffer,
+ testing::MatchResultListener* /* listener */) const {
+ FramerVisitorCapturingPublicReset visitor;
+ QuicFramer framer(QuicVersionMax(),
+ QuicTime::Zero(),
+ false);
+ framer.set_visitor(&visitor);
+ QuicEncryptedPacket encrypted(std::tr1::get<0>(packet_buffer),
+ std::tr1::get<1>(packet_buffer));
+ framer.ProcessPacket(encrypted);
+ QuicPublicResetPacket packet = visitor.public_reset_packet();
+ return guid_ == packet.public_header.guid &&
+ packet.public_header.reset_flag && !packet.public_header.version_flag &&
+ sequence_number_ == packet.rejected_sequence_number;
+ }
+
+ virtual void DescribeTo(::std::ostream* os) const { }
+
+ virtual void DescribeNegationTo(::std::ostream* os) const { }
+
+ private:
+ QuicGuid guid_;
+ QuicPacketSequenceNumber sequence_number_;
+};
+
+void ValidPublicResetPacketPredicate(
+ QuicGuid expected_guid,
+ QuicPacketSequenceNumber expected_sequence_number,
+ const std::tr1::tuple<const char*, int>& packet_buffer) {
+ FramerVisitorCapturingPublicReset visitor;
+ QuicFramer framer(QuicVersionMax(),
+ QuicTime::Zero(),
+ false);
+ framer.set_visitor(&visitor);
+ QuicEncryptedPacket encrypted(std::tr1::get<0>(packet_buffer),
+ std::tr1::get<1>(packet_buffer));
+ framer.ProcessPacket(encrypted);
+ QuicPublicResetPacket packet = visitor.public_reset_packet();
+ EXPECT_EQ(expected_guid, packet.public_header.guid);
+ EXPECT_TRUE(packet.public_header.reset_flag);
+ EXPECT_FALSE(packet.public_header.version_flag);
+ EXPECT_EQ(expected_sequence_number, packet.rejected_sequence_number);
+}
+
+
+Matcher<const std::tr1::tuple<const char*, int> > PublicResetPacketEq(
+ QuicGuid guid,
+ QuicPacketSequenceNumber sequence_number) {
+ return MakeMatcher(new ValidatePublicResetPacketPredicate(guid,
+ sequence_number));
+}
+
+TEST_F(QuicTimeWaitListManagerTest, CheckGuidInTimeWait) {
+ EXPECT_FALSE(IsGuidInTimeWait(guid_));
+ AddGuid(guid_);
+ EXPECT_TRUE(IsGuidInTimeWait(guid_));
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendPublicReset) {
+ AddGuid(guid_);
+ const int kRandomSequenceNumber = 1;
+ scoped_ptr<QuicEncryptedPacket> packet(
+ ConstructEncryptedPacket(guid_, kRandomSequenceNumber));
+ EXPECT_CALL(writer_, WritePacket(_, _,
+ server_address_.address(),
+ client_address_,
+ &time_wait_list_manager_,
+ _))
+ .With(Args<0, 1>(PublicResetPacketEq(guid_,
+ kRandomSequenceNumber)))
+ .WillOnce(Return(packet->length()));
+
+ ProcessPacket(guid_, *packet);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, DropInvalidPacket) {
+ AddGuid(guid_);
+ const char buffer[] = "invalid";
+ QuicEncryptedPacket packet(buffer, arraysize(buffer));
+ ProcessPacket(guid_, packet);
+ // Will get called for a valid packet since received packet count = 1 (2 ^ 0).
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _)).Times(0);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, DropPublicResetPacket) {
+ AddGuid(guid_);
+ QuicPublicResetPacket packet;
+ packet.public_header.guid = guid_;
+ packet.public_header.version_flag = false;
+ packet.public_header.reset_flag = true;
+ packet.rejected_sequence_number = 239191;
+ packet.nonce_proof = 1010101;
+ scoped_ptr<QuicEncryptedPacket> public_reset_packet(
+ QuicFramer::BuildPublicResetPacket(packet));
+ ProcessPacket(guid_, *public_reset_packet);
+ // Will get called for a data packet since received packet count = 1 (2 ^ 0).
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _))
+ .Times(0);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) {
+ AddGuid(guid_);
+ for (int sequence_number = 1; sequence_number < 101; ++sequence_number) {
+ scoped_ptr<QuicEncryptedPacket> packet(
+ ConstructEncryptedPacket(guid_, sequence_number));
+ if ((sequence_number & (sequence_number - 1)) == 0) {
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _))
+ .WillOnce(Return(1));
+ }
+ ProcessPacket(guid_, *packet);
+ // Send public reset with exponential back off.
+ if ((sequence_number & (sequence_number - 1)) == 0) {
+ EXPECT_TRUE(
+ time_wait_list_manager_.ShouldSendPublicReset(sequence_number));
+ } else {
+ EXPECT_FALSE(
+ time_wait_list_manager_.ShouldSendPublicReset(sequence_number));
+ }
+ }
+}
+
+TEST_F(QuicTimeWaitListManagerTest, CleanUpOldGuids) {
+ const int kGuidCount = 100;
+ const int kOldGuidCount = 31;
+
+ // Add guids such that their expiry time is kTimeWaitPeriod_.
+ epoll_server_.set_now_in_usec(0);
+ for (int guid = 1; guid <= kOldGuidCount; ++guid) {
+ AddGuid(guid);
+ }
+
+ // Add remaining guids such that their add time is 2 * kTimeWaitPeriod.
+ const QuicTime::Delta time_wait_period =
+ time_wait_list_manager_.time_wait_period();
+ epoll_server_.set_now_in_usec(time_wait_period.ToMicroseconds());
+ for (int guid = kOldGuidCount + 1; guid <= kGuidCount; ++guid) {
+ AddGuid(guid);
+ }
+
+ QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39);
+ // Now set the current time as time_wait_period + offset usecs.
+ epoll_server_.set_now_in_usec(time_wait_period.Add(offset).ToMicroseconds());
+ // After all the old guids are cleaned up, check the next alarm interval.
+ int64 next_alarm_time = epoll_server_.ApproximateNowInUsec() +
+ time_wait_period.Subtract(offset).ToMicroseconds();
+ EXPECT_CALL(epoll_server_, RegisterAlarm(next_alarm_time, _));
+
+ time_wait_list_manager_.CleanUpOldGuids();
+ for (int guid = 1; guid <= kGuidCount; ++guid) {
+ EXPECT_EQ(guid > kOldGuidCount, IsGuidInTimeWait(guid))
+ << "kOldGuidCount: " << kOldGuidCount
+ << " guid: " << guid;
+ }
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendQueuedPackets) {
+ QuicGuid guid = 1;
+ AddGuid(guid);
+ QuicPacketSequenceNumber sequence_number = 234;
+ scoped_ptr<QuicEncryptedPacket> packet(
+ ConstructEncryptedPacket(guid, sequence_number));
+ // Let first write through.
+ EXPECT_CALL(writer_, WritePacket(_, _,
+ server_address_.address(),
+ client_address_,
+ &time_wait_list_manager_,
+ _))
+ .With(Args<0, 1>(PublicResetPacketEq(guid,
+ sequence_number)))
+ .WillOnce(Return(packet->length()));
+ ProcessPacket(guid, *packet);
+ EXPECT_FALSE(time_wait_list_manager_.is_write_blocked());
+
+ // write block for the next packet.
+ EXPECT_CALL(writer_, WritePacket(_, _,
+ server_address_.address(),
+ client_address_,
+ &time_wait_list_manager_,
+ _))
+ .With(Args<0, 1>(PublicResetPacketEq(guid,
+ sequence_number)))
+ .WillOnce(DoAll(SetArgPointee<5>(EAGAIN), Return(-1)));
+ ProcessPacket(guid, *packet);
+ // 3rd packet. No public reset should be sent;
+ ProcessPacket(guid, *packet);
+ EXPECT_TRUE(time_wait_list_manager_.is_write_blocked());
+
+ // write packet should not be called since already write blocked but the
+ // should be queued.
+ QuicGuid other_guid = 2;
+ AddGuid(other_guid);
+ QuicPacketSequenceNumber other_sequence_number = 23423;
+ scoped_ptr<QuicEncryptedPacket> other_packet(
+ ConstructEncryptedPacket(other_guid, other_sequence_number));
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _))
+ .Times(0);
+ ProcessPacket(other_guid, *other_packet);
+
+ // Now expect all the write blocked public reset packets to be sent again.
+ EXPECT_CALL(writer_, WritePacket(_, _,
+ server_address_.address(),
+ client_address_,
+ &time_wait_list_manager_,
+ _))
+ .With(Args<0, 1>(PublicResetPacketEq(guid,
+ sequence_number)))
+ .WillOnce(Return(packet->length()));
+ EXPECT_CALL(writer_, WritePacket(_, _,
+ server_address_.address(),
+ client_address_,
+ &time_wait_list_manager_,
+ _))
+ .With(Args<0, 1>(PublicResetPacketEq(other_guid,
+ other_sequence_number)))
+ .WillOnce(Return(other_packet->length()));
+ time_wait_list_manager_.OnCanWrite();
+ EXPECT_FALSE(time_wait_list_manager_.is_write_blocked());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, MakeSureFramerUsesCorrectVersion) {
+ const int kRandomSequenceNumber = 1;
+ scoped_ptr<QuicEncryptedPacket> packet;
+
+ AddGuid(guid_, QuicVersionMin());
+ framer_.set_version(QuicVersionMin());
+ packet.reset(ConstructEncryptedPacket(guid_, kRandomSequenceNumber));
+
+ // Reset packet should be written, using the minimum quic version.
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _)).Times(1);
+ ProcessPacket(guid_, *packet);
+ EXPECT_EQ(time_wait_list_manager_.version(), QuicVersionMin());
+
+ // New guid
+ ++guid_;
+
+ AddGuid(guid_, QuicVersionMax());
+ framer_.set_version(QuicVersionMax());
+ packet.reset(ConstructEncryptedPacket(guid_, kRandomSequenceNumber));
+
+ // Reset packet should be written, using the maximum quic version.
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _, _)).Times(1);
+ ProcessPacket(guid_, *packet);
+ EXPECT_EQ(time_wait_list_manager_.version(), QuicVersionMax());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, GetQuicVersionFromMap) {
+ const int kGuid1 = 123;
+ const int kGuid2 = 456;
+ const int kGuid3 = 789;
+
+ AddGuid(kGuid1, QuicVersionMin());
+ AddGuid(kGuid2, QuicVersionMax());
+ AddGuid(kGuid3, QuicVersionMax());
+
+ EXPECT_EQ(QuicVersionMin(),
+ time_wait_list_manager_.GetQuicVersionFromGuid(kGuid1));
+ EXPECT_EQ(QuicVersionMax(),
+ time_wait_list_manager_.GetQuicVersionFromGuid(kGuid2));
+ EXPECT_EQ(QuicVersionMax(),
+ time_wait_list_manager_.GetQuicVersionFromGuid(kGuid3));
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 859d7a5604..272786ebf4 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -11,6 +11,7 @@
#include "net/quic/crypto/proof_verifier.h"
#include "net/tools/flip_server/balsa_headers.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_spdy_client_stream.h"
#include "net/tools/quic/test_tools/http_message_test_utils.h"
#include "url/gurl.h"
@@ -26,7 +27,6 @@ class RecordingProofVerifier : public net::ProofVerifier {
public:
// ProofVerifier interface.
virtual net::ProofVerifier::Status VerifyProof(
- net::QuicVersion version,
const string& hostname,
const string& server_config,
const vector<string>& certs,
@@ -158,6 +158,7 @@ void QuicTestClient::Initialize(IPEndPoint address,
server_address_ = address;
stream_ = NULL;
stream_error_ = QUIC_STREAM_NO_ERROR;
+ priority_ = 3;
bytes_read_ = 0;
bytes_written_= 0;
never_connected_ = true;
@@ -244,10 +245,13 @@ QuicReliableClientStream* QuicTestClient::GetOrCreateStream() {
}
if (!stream_) {
stream_ = client_->CreateReliableClientStream();
- if (stream_ != NULL) {
- stream_->set_visitor(this);
+ if (stream_ == NULL) {
+ return NULL;
}
+ stream_->set_visitor(this);
+ reinterpret_cast<QuicSpdyClientStream*>(stream_)->set_priority(priority_);
}
+
return stream_;
}
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 74bfc24646..3cd71d59f8 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -107,6 +107,8 @@ class QuicTestClient : public ReliableQuicStream::Visitor {
void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
+ void set_priority(QuicPriority priority) { priority_ = priority; }
+
private:
void Initialize(IPEndPoint address, const string& hostname, bool secure);
@@ -118,6 +120,8 @@ class QuicTestClient : public ReliableQuicStream::Visitor {
QuicRstStreamErrorCode stream_error_;
BalsaHeaders headers_;
+ QuicPriority priority_;
+
string response_;
uint64 bytes_read_;
uint64 bytes_written_;
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index 481a57f833..fa3627ba74 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -50,6 +50,13 @@ void MockConnection::AdvanceTime(QuicTime::Delta delta) {
static_cast<MockHelper*>(helper())->AdvanceTime(delta);
}
+
+MockQuicSessionOwner::MockQuicSessionOwner() {
+}
+
+MockQuicSessionOwner::~MockQuicSessionOwner() {
+}
+
bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
data.AppendToString(&data_);
return true;
@@ -82,6 +89,12 @@ MockAckNotifierDelegate::MockAckNotifierDelegate() {
MockAckNotifierDelegate::~MockAckNotifierDelegate() {
}
+MockPacketWriter::MockPacketWriter() {
+}
+
+MockPacketWriter::~MockPacketWriter() {
+}
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index ba33c8bbfe..dffa2558af 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -12,6 +12,8 @@
#include "net/quic/quic_session.h"
#include "net/quic/quic_spdy_decompressor.h"
#include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/quic_packet_writer.h"
+#include "net/tools/quic/quic_server_session.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace net {
@@ -69,6 +71,13 @@ class MockConnection : public QuicConnection {
DISALLOW_COPY_AND_ASSIGN(MockConnection);
};
+class MockQuicSessionOwner : public QuicSessionOwner {
+ public:
+ MockQuicSessionOwner();
+ ~MockQuicSessionOwner();
+ MOCK_METHOD2(OnConnectionClose, void(QuicGuid guid, QuicErrorCode error));
+};
+
class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
public:
virtual ~TestDecompressorVisitor() {}
@@ -111,6 +120,19 @@ class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
MOCK_METHOD0(OnAckNotification, void());
};
+class MockPacketWriter : public QuicPacketWriter {
+ public:
+ MockPacketWriter();
+ virtual ~MockPacketWriter();
+
+ MOCK_METHOD6(WritePacket, int(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* blocked_writer,
+ int* error));
+};
+
} // namespace test
} // namespace tools
} // namespace net
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index f343f70e8f..e0317e8868 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -1348,7 +1348,7 @@ class TestPageHandler(testserver_base.BasePageHandler):
if query_char < 0 or len(self.path) <= query_char + 1:
self.sendRedirectHelp(test_name)
return True
- dest = self.path[query_char + 1:]
+ dest = urllib.unquote(self.path[query_char + 1:])
self.send_response(301) # moved permanently
self.send_header('Location', dest)
@@ -1372,7 +1372,7 @@ class TestPageHandler(testserver_base.BasePageHandler):
if query_char < 0 or len(self.path) <= query_char + 1:
self.sendRedirectHelp(test_name)
return True
- dest = self.path[query_char + 1:]
+ dest = urllib.unquote(self.path[query_char + 1:])
self.send_response(200)
self.send_header('Content-Type', 'text/html')
diff --git a/net/url_request/test_url_fetcher_factory.cc b/net/url_request/test_url_fetcher_factory.cc
index e0394c44d1..30380354b2 100644
--- a/net/url_request/test_url_fetcher_factory.cc
+++ b/net/url_request/test_url_fetcher_factory.cc
@@ -357,11 +357,18 @@ URLFetcher* FakeURLFetcherFactory::CreateURLFetcher(
return fake_fetcher.release();
}
+void FakeURLFetcherFactory::SetFakeResponseForURL(
+ const GURL& url,
+ const std::string& response_data,
+ bool success) {
+ // Overwrite existing URL if it already exists.
+ fake_responses_[url] = std::make_pair(response_data, success);
+}
+
void FakeURLFetcherFactory::SetFakeResponse(const std::string& url,
const std::string& response_data,
bool success) {
- // Overwrite existing URL if it already exists.
- fake_responses_[GURL(url)] = std::make_pair(response_data, success);
+ SetFakeResponseForURL(GURL(url), response_data, success);
}
void FakeURLFetcherFactory::ClearFakeResponses() {
diff --git a/net/url_request/test_url_fetcher_factory.h b/net/url_request/test_url_fetcher_factory.h
index 89f74c2f59..35b4607f99 100644
--- a/net/url_request/test_url_fetcher_factory.h
+++ b/net/url_request/test_url_fetcher_factory.h
@@ -377,6 +377,12 @@ class FakeURLFetcherFactory : public URLFetcherFactory,
// Sets the fake response for a given URL. If success is true we will serve
// an HTTP/200 and an HTTP/500 otherwise. The |response_data| may be empty.
+ void SetFakeResponseForURL(const GURL& url,
+ const std::string& response_data,
+ bool success);
+
+ // Convenience helper that calls SetFakeResponseForURL with GURL(url).
+ // TODO(mnissler): Convert callers to SetFakeResponseForURL.
void SetFakeResponse(const std::string& url,
const std::string& response_data,
bool success);
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 669c845de9..bf4aafcb8c 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -319,6 +319,10 @@ void URLRequestJob::NotifyHeadersComplete() {
new_location = new_location.ReplaceComponents(replacements);
}
+ // Redirect response bodies are not read. Notify the transaction
+ // so it does not treat being stopped as an error.
+ DoneReading();
+
bool defer_redirect = false;
request_->NotifyReceivedRedirect(new_location, &defer_redirect);
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index 354915fe12..5f63b0927d 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -37,6 +37,24 @@ const MockTransaction kGZip_Transaction = {
net::OK
};
+const MockTransaction kRedirect_Transaction = {
+ "http://www.google.com/redirect",
+ "GET",
+ base::Time(),
+ "",
+ net::LOAD_NORMAL,
+ "HTTP/1.1 302 Found",
+ "Cache-Control: max-age=10000\n"
+ "Location: http://www.google.com/destination\n"
+ "Content-Length: 5\n",
+ base::Time(),
+ "hello",
+ TEST_MODE_NORMAL,
+ NULL,
+ 0,
+ net::OK
+};
+
} // namespace
TEST(URLRequestJob, TransactionNotifiedWhenDone) {
@@ -78,3 +96,22 @@ TEST(URLRequestJob, SyncTransactionNotifiedWhenDone) {
RemoveMockTransaction(&transaction);
}
+
+TEST(URLRequestJob, RedirectTransactionNotifiedWhenDone) {
+ MockNetworkLayer network_layer;
+ net::TestURLRequestContext context;
+ context.set_http_transaction_factory(&network_layer);
+
+ net::TestDelegate d;
+ net::TestURLRequest req(GURL(kRedirect_Transaction.url), &d, &context, NULL);
+ AddMockTransaction(&kRedirect_Transaction);
+
+ req.set_method("GET");
+ req.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_TRUE(network_layer.done_reading_called());
+
+ RemoveMockTransaction(&kRedirect_Transaction);
+}
diff --git a/net/websockets/README b/net/websockets/README
index 25b3e84315..1d1e1c3538 100644
--- a/net/websockets/README
+++ b/net/websockets/README
@@ -40,6 +40,11 @@ websocket_deflater.cc
websocket_deflater_test.cc
websocket_errors.cc
websocket_errors.h
+websocket_extension.cc
+websocket_extension.h
+websocket_extension_parser.cc
+websocket_extension_parser.h
+websocket_extension_parser_test.cc
websocket_errors_test.cc
websocket_event_interface.h
websocket_frame.cc
diff --git a/net/websockets/websocket_extension.cc b/net/websockets/websocket_extension.cc
new file mode 100644
index 0000000000..edcd8e8657
--- /dev/null
+++ b/net/websockets/websocket_extension.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_extension.h"
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace net {
+
+WebSocketExtension::Parameter::Parameter(const std::string& name)
+ : name_(name) {}
+
+WebSocketExtension::Parameter::Parameter(const std::string& name,
+ const std::string& value)
+ : name_(name), value_(value) {
+ DCHECK(!value.empty());
+}
+
+bool WebSocketExtension::Parameter::Equals(const Parameter& other) const {
+ return name_ == other.name_ && value_ == other.value_;
+}
+
+WebSocketExtension::WebSocketExtension() {}
+
+WebSocketExtension::WebSocketExtension(const std::string& name)
+ : name_(name) {}
+
+WebSocketExtension::~WebSocketExtension() {}
+
+bool WebSocketExtension::Equals(const WebSocketExtension& other) const {
+ if (name_ != other.name_) return false;
+ if (parameters_.size() != other.parameters_.size()) return false;
+ for (size_t i = 0; i < other.parameters_.size(); ++i) {
+ if (!parameters_[i].Equals(other.parameters_[i]))
+ return false;
+ }
+ return true;
+}
+
+} // namespace net
diff --git a/net/websockets/websocket_extension.h b/net/websockets/websocket_extension.h
new file mode 100644
index 0000000000..5af4023869
--- /dev/null
+++ b/net/websockets/websocket_extension.h
@@ -0,0 +1,57 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_WEBSOCKETS_WEBSOCKET_EXTENSION_H_
+#define NET_WEBSOCKETS_WEBSOCKET_EXTENSION_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// A WebSocketExtension instance represents a WebSocket extension specified
+// in RFC6455.
+class NET_EXPORT_PRIVATE WebSocketExtension {
+ public:
+ // Note that RFC6455 does not allow a parameter with an empty value.
+ class NET_EXPORT_PRIVATE Parameter {
+ public:
+ // Construct a parameter which does not have a value.
+ explicit Parameter(const std::string& name);
+ // Construct a parameter with a non-empty value.
+ Parameter(const std::string& name, const std::string& value);
+
+ bool HasValue() const { return !value_.empty(); }
+ const std::string& name() const { return name_; }
+ const std::string& value() const { return value_; }
+ bool Equals(const Parameter& other) const;
+
+ // The default copy constructor and the assignment operator are defined:
+ // we need them.
+ private:
+ std::string name_;
+ std::string value_;
+ };
+
+ WebSocketExtension();
+ explicit WebSocketExtension(const std::string& name);
+ ~WebSocketExtension();
+
+ void Add(const Parameter& parameter) { parameters_.push_back(parameter); }
+ const std::string& name() const { return name_; }
+ const std::vector<Parameter>& parameters() const { return parameters_; }
+ bool Equals(const WebSocketExtension& other) const;
+
+ // The default copy constructor and the assignment operator are defined:
+ // we need them.
+ private:
+ std::string name_;
+ std::vector<Parameter> parameters_;
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_EXTENSION_H_
diff --git a/net/websockets/websocket_extension_parser.cc b/net/websockets/websocket_extension_parser.cc
new file mode 100644
index 0000000000..28a2db16f2
--- /dev/null
+++ b/net/websockets/websocket_extension_parser.cc
@@ -0,0 +1,158 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_extension_parser.h"
+
+#include "base/strings/string_util.h"
+
+namespace net {
+
+WebSocketExtensionParser::WebSocketExtensionParser() {}
+
+WebSocketExtensionParser::~WebSocketExtensionParser() {}
+
+void WebSocketExtensionParser::Parse(const char* data, size_t size) {
+ current_ = data;
+ end_ = data + size;
+ has_error_ = false;
+
+ ConsumeExtension(&extension_);
+ if (has_error_) return;
+ ConsumeSpaces();
+ has_error_ = has_error_ || (current_ != end_);
+}
+
+void WebSocketExtensionParser::Consume(char c) {
+ DCHECK(!has_error_);
+ ConsumeSpaces();
+ DCHECK(!has_error_);
+ if (current_ == end_ || c != current_[0]) {
+ has_error_ = true;
+ return;
+ }
+ ++current_;
+}
+
+void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) {
+ DCHECK(!has_error_);
+ base::StringPiece name;
+ ConsumeToken(&name);
+ if (has_error_) return;
+ *extension = WebSocketExtension(name.as_string());
+
+ while (ConsumeIfMatch(';')) {
+ WebSocketExtension::Parameter parameter((std::string()));
+ ConsumeExtensionParameter(&parameter);
+ if (has_error_) return;
+ extension->Add(parameter);
+ }
+}
+
+void WebSocketExtensionParser::ConsumeExtensionParameter(
+ WebSocketExtension::Parameter* parameter) {
+ DCHECK(!has_error_);
+ base::StringPiece name, value;
+ std::string value_string;
+
+ ConsumeToken(&name);
+ if (has_error_) return;
+ if (!ConsumeIfMatch('=')) {
+ *parameter = WebSocketExtension::Parameter(name.as_string());
+ return;
+ }
+
+ if (Lookahead('\"')) {
+ ConsumeQuotedToken(&value_string);
+ } else {
+ ConsumeToken(&value);
+ value_string = value.as_string();
+ }
+ if (has_error_) return;
+ *parameter = WebSocketExtension::Parameter(name.as_string(), value_string);
+}
+
+void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) {
+ DCHECK(!has_error_);
+ ConsumeSpaces();
+ DCHECK(!has_error_);
+ const char* head = current_;
+ while (current_ < end_ &&
+ !IsControl(current_[0]) && !IsSeparator(current_[0]))
+ ++current_;
+ if (current_ == head) {
+ has_error_ = true;
+ return;
+ }
+ *token = base::StringPiece(head, current_ - head);
+}
+
+void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) {
+ DCHECK(!has_error_);
+ Consume('"');
+ if (has_error_) return;
+ *token = "";
+ while (current_ < end_ && !IsControl(current_[0])) {
+ if (UnconsumedBytes() >= 2 && current_[0] == '\\') {
+ char next = current_[1];
+ if (IsControl(next) || IsSeparator(next)) break;
+ *token += next;
+ current_ += 2;
+ } else if (IsSeparator(current_[0])) {
+ break;
+ } else {
+ *token += current_[0];
+ ++current_;
+ }
+ }
+ // We can't use Consume here because we don't want to consume spaces.
+ if (current_ < end_ && current_[0] == '"')
+ ++current_;
+ else
+ has_error_ = true;
+ has_error_ = has_error_ || token->empty();
+}
+
+void WebSocketExtensionParser::ConsumeSpaces() {
+ DCHECK(!has_error_);
+ while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t'))
+ ++current_;
+ return;
+}
+
+bool WebSocketExtensionParser::Lookahead(char c) {
+ DCHECK(!has_error_);
+ const char* head = current_;
+
+ Consume(c);
+ bool result = !has_error_;
+ current_ = head;
+ has_error_ = false;
+ return result;
+}
+
+bool WebSocketExtensionParser::ConsumeIfMatch(char c) {
+ DCHECK(!has_error_);
+ const char* head = current_;
+
+ Consume(c);
+ if (has_error_) {
+ current_ = head;
+ has_error_ = false;
+ return false;
+ }
+ return true;
+}
+
+// static
+bool WebSocketExtensionParser::IsControl(char c) {
+ return (0 <= c && c <= 31) || c == 127;
+}
+
+// static
+bool WebSocketExtensionParser::IsSeparator(char c) {
+ const char separators[] = "()<>@,;:\\\"/[]?={} \t";
+ return strchr(separators, c) != NULL;
+}
+
+} // namespace net
diff --git a/net/websockets/websocket_extension_parser.h b/net/websockets/websocket_extension_parser.h
new file mode 100644
index 0000000000..ef7fe03665
--- /dev/null
+++ b/net/websockets/websocket_extension_parser.h
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_WEBSOCKETS_WEBSOCKET_EXTENSION_PARSER_H_
+#define NET_WEBSOCKETS_WEBSOCKET_EXTENSION_PARSER_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/websockets/websocket_extension.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE WebSocketExtensionParser {
+ public:
+ WebSocketExtensionParser();
+ ~WebSocketExtensionParser();
+
+ // Parses the given string as a WebSocket extension header value.
+ // This parser assumes some preprocesses are made.
+ // - The parser parses single extension at a time. This means that
+ // the parser parses |extension| in RFC6455 9.1, not |extension-list|.
+ // - There is no newline characters in the input. LWS-concatenation must
+ // have already been done.
+ void Parse(const char* data, size_t size);
+ void Parse(const std::string& data) {
+ Parse(data.data(), data.size());
+ }
+
+ bool has_error() const { return has_error_; }
+ const WebSocketExtension& extension() const { return extension_; }
+
+ private:
+ void Consume(char c);
+ void ConsumeExtension(WebSocketExtension* extension);
+ void ConsumeExtensionParameter(WebSocketExtension::Parameter* parameter);
+ void ConsumeToken(base::StringPiece* token);
+ void ConsumeQuotedToken(std::string* token);
+ void ConsumeSpaces();
+ bool Lookahead(char c);
+ bool ConsumeIfMatch(char c);
+ size_t UnconsumedBytes() const { return end_ - current_; }
+
+ static bool IsControl(char c);
+ static bool IsSeparator(char c);
+
+ const char* current_;
+ const char* end_;
+ bool has_error_;
+ WebSocketExtension extension_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketExtensionParser);
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_EXTENSION_PARSER_H_
diff --git a/net/websockets/websocket_extension_parser_test.cc b/net/websockets/websocket_extension_parser_test.cc
new file mode 100644
index 0000000000..dc7dc859d8
--- /dev/null
+++ b/net/websockets/websocket_extension_parser_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_extension_parser.h"
+
+#include <string>
+
+#include "net/websockets/websocket_extension.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(WebSocketExtensionParserTest, ParseEmpty) {
+ WebSocketExtensionParser parser;
+ parser.Parse("", 0);
+
+ EXPECT_TRUE(parser.has_error());
+}
+
+TEST(WebSocketExtensionParserTest, ParseSimple) {
+ WebSocketExtensionParser parser;
+ WebSocketExtension expected("foo");
+
+ parser.Parse("foo");
+
+ ASSERT_FALSE(parser.has_error());
+ EXPECT_TRUE(expected.Equals(parser.extension()));
+}
+
+TEST(WebSocketExtensionParserTest, ParseOneExtensionWithOneParamWithoutValue) {
+ WebSocketExtensionParser parser;
+ WebSocketExtension expected("foo");
+ expected.Add(WebSocketExtension::Parameter("bar"));
+
+ parser.Parse("\tfoo ; bar");
+
+ ASSERT_FALSE(parser.has_error());
+ EXPECT_TRUE(expected.Equals(parser.extension()));
+}
+
+TEST(WebSocketExtensionParserTest, ParseOneExtensionWithOneParamWithValue) {
+ WebSocketExtensionParser parser;
+ WebSocketExtension expected("foo");
+ expected.Add(WebSocketExtension::Parameter("bar", "baz"));
+
+ parser.Parse("foo ; bar= baz\t");
+
+ ASSERT_FALSE(parser.has_error());
+ EXPECT_TRUE(expected.Equals(parser.extension()));
+}
+
+TEST(WebSocketExtensionParserTest, ParseOneExtensionWithParams) {
+ WebSocketExtensionParser parser;
+ WebSocketExtension expected("foo");
+ expected.Add(WebSocketExtension::Parameter("bar", "baz"));
+ expected.Add(WebSocketExtension::Parameter("hoge", "fuga"));
+
+ parser.Parse("foo ; bar= baz;\t \thoge\t\t=fuga");
+
+ ASSERT_FALSE(parser.has_error());
+ EXPECT_TRUE(expected.Equals(parser.extension()));
+}
+
+TEST(WebSocketExtensionParserTest, InvalidPatterns) {
+ const char* patterns[] = {
+ "fo\ao", // control in extension name
+ "fo\x01o", // control in extension name
+ "fo<o", // separator in extension name
+ "foo/", // separator in extension name
+ ";bar", // empty extension name
+ "foo bar", // missing ';'
+ "foo;", // extension parameter without name and value
+ "foo; b\ar", // control in parameter name
+ "foo; b\x7fr", // control in parameter name
+ "foo; b[r", // separator in parameter name
+ "foo; ba:", // separator in parameter name
+ "foo; =baz", // empty parameter name
+ "foo; bar=", // empty parameter value
+ "foo; =", // empty parameter name and value
+ "foo; bar=b\x02z", // control in parameter value
+ "foo; bar=b@z", // separator in parameter value
+ "foo; bar=b\\z", // separator in parameter value
+ "foo; bar=b?z", // separator in parameter value
+ "\"foo\"", // quoted extension name
+ "foo; \"bar\"", // quoted parameter name
+ "foo; bar=\"\a2\"", // control in quoted parameter value
+ "foo; bar=\"b@z\"", // separator in quoted parameter value
+ "foo; bar=\"b\\\\z\"", // separator in quoted parameter value
+ "foo; bar=\"\"", // quoted empty parameter value
+ "foo; bar=\"baz", // unterminated quoted string
+ "foo; bar=\"baz \"", // space in quoted string
+ "foo; bar baz", // mising '='
+ "foo; bar - baz", // '-' instead of '=' (note: "foo; bar-baz" is valid).
+ "foo; bar=\r\nbaz", // CRNL not followed by a space
+ "foo; bar=\r\n baz", // CRNL followed by a space
+ "foo, bar" // multiple extensions
+ };
+
+ for (size_t i = 0; i < arraysize(patterns); ++i) {
+ WebSocketExtensionParser parser;
+ parser.Parse(patterns[i]);
+ EXPECT_TRUE(parser.has_error());
+ }
+}
+
+TEST(WebSocketExtensionParserTest, QuotedParameterValue) {
+ WebSocketExtensionParser parser;
+ WebSocketExtension expected("foo");
+ expected.Add(WebSocketExtension::Parameter("bar", "baz"));
+
+ parser.Parse("foo; bar = \"ba\\z\" ");
+
+ ASSERT_FALSE(parser.has_error());
+ EXPECT_TRUE(expected.Equals(parser.extension()));
+}
+
+} // namespace
+
+} // namespace net